ETH Price: $2,345.16 (+2.49%)

Transaction Decoder

Block:
14863155 at May-28-2022 11:33:43 PM +UTC
Transaction Fee:
0.02231095488758493 ETH $52.32
Gas Used:
1,348,170 Gas / 16.549066429 Gwei

Emitted Events:

314 Tickets.OwnershipTransferred( previousOwner=0x00000000...000000000, newOwner=[Receiver] TicketBooth )
315 TicketBooth.Issue( projectId=568, name=LunchDao, symbol=LUNCH, caller=[Sender] 0x9e84aaafe42302252a0378d016ae63a19f49e46a )

Account State Difference:

  Address   Before After State Difference Code
(Hiveon Pool)
18,765.760167432424824908 Eth18,765.762189687424824908 Eth0.002022255
0x71a28feA...7D421e7e0
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 3324205854995707979903630682322104006550589137901166512799259902213205733288761989854795760229979905399239641899144503158106484263453542236434097726084352193719893834545591444314219793544953837090388924764489850453395435105443870241841613654602409145282576338785273923154156218688803941071112499955449334840840840024814683843727658152656369001749204727885994541818775718827906735840058589601887905553621868004205002563921890878317817372068863306268510075822089534103098667829577085358428788329222035326652129372732924435911143008893006057540895699415345142152281223900782337286566725255873077380249394478520190786484021089313886109221389306727822179271042272866132561638795976360431118976744154263049125744363369567090523411060555449257511946929070770345392856720618948356940497826475284982308979020118984203125884269025344420722011914987079976917947073401138611800100041321564581622187956124993763198606211035535412454104857744019307443875793964145195024843604355721813333831769258519041203341871762255351256883176394111478783933987049173124048144056445001624385036744468432224574155360658370443324432866652812548322975582899458240488505922709674523101986582448278087420778613037368426691395870842626275075099207781017092038255675693332253360634876536120122970591965597949004701753979427602199048123940266523681621097926588678830015047580837951837579510306081642641530962003099573301284976638046444545851986861618638824490825643716423223917287728694608996928883108064493214257991829016228389771789755327196464393356447421353750566850798220806423984897948766207075737145234104135450302333527381450704243899588351561603066025353109314537975499692256287126691969449183285393916904764710773294408736902341198321016682529586298149746918638078551509862121763864234561108151949353134752921844642980593333630576672614314681901832987708698176034385753854389539513525514980869559700381631514246841342741969988650732461594608861086885671594451584727620684660675611755564973631519049657147782380819728956923628684788977017078828911480468187206950507855382695558730324547358460864724707462118174757983663480845910901234046355539361619573571131943803530553932554791646685604184590961236987943572732937200072052256840806194943569273073121078263193291954289364102920375225491736241709825322552503267634621534433740608059012014742887985260144905395469808331359830267253143192494967506935031726766387008454539284260420002509309656640839250447414297654568677730667044938364423181904553358605088189586547765349655342328073016601458847003430007132477393687044425064076356212612584079908389113270864156225454069077525325256655675807677163469502902082280932198705110044163582992634597822581473134924933919240487839245683477292330105831069670989492875638462880052709136828410073850035083651655934013415191500734707397894759125229213254006552847688101576128905727407578145871689220310485253488029113770257623164766423407136231699057075951687903691220341236055228245509855495784652756765697141341866237749592040801615962905950365695880243426860421103429249699354380593801669115409556213882299084122856159432782338375574075594273662439104575944622681616207563873555504105224099940240517630851227141594095673479344864936444320860240827128832481066771124766429518110696730565797037494984069999759831451141787654913505827065535376026446266892348303602684115288130924209359952190453198566217842881031165774208315372426958002053493515430570029642459968421903331708109773105573462267507413403962412421440257424449854357037608378209232359064397863821611028813446518002629641662152298567917122455584276836223055050772107461988725829095702842829305190163937706570960902766289217702608455996077144965287470763603390279592101045835626208131185541491151106658726786541732590363482338698121088718525768459133293983944890470087715791490590471008215049648626384287958461377042248011557568790102889315970854974581743217905574114753024157676877030145693159577304224873515398367107601978131787434289345461227622434371246261456802047890191710874490863345773145370676272691345192082664461129376487185596281076749572026483386354015397987203550313925872604476461671060756730102003942771697468209635708301833872866293764695770605040211057845442336309768173313786144981146377921141095942389239194920117816516453950231758527734618497879380518507129035772397211898335954571829819224596225865760899796389522101279653085771152874669154858212270175814404505303505300684060675747560333341253480097839708131096640857436910017370420999020641304408673219590653911036218175916418902979736177710546786173130815412644528120835052704112589162464216019307368682475469207643196206409564722765672588136655876449578983968067220786339007770092873776813909096414323048272112193756610388242287452379644675404119008472814087383152410852865483731408714904190875722573539983928609792046699581924294635675896228459097025069436921735118897431645932838843750753417168381968004147920150123988629962878302301435953383490001519543717762256297146689087643470926468342154642652700559891817532198864400789440788950637404718921431880639944482137330627280336185947512715222286234918803334825769846669309842160011193929487247180838103593409720965456980627538238252868732299273804215331402044918023494369147350553664824531402136889437534496615265071482902968560323124306103333299374394673785008544558479959853825802864187789648285535624703248191666394827367355298058099574982730480112185178323956121625328711221365454175183261698989256380291260417347219528874624467372315830035750476213584322001622140121634847226865282704482980512173723316305616337646800707187785437743093073856582962038666480375716717974579758734467329829569022127022539045903621180041656499578944591159465666888791270456900846672394870032676486534297866205335719149858237435165999287335903547571835807861231429998536506606808617507211585309445389783633956526079105028200777988219841068727631326903091289613662830714965217625086895338984416873527588221974374392672498224899669260908751684989397936789501027189256668589047310118676662479930377705434447073586383614786390499935691152023986719751714703889125683958213106068631730122910264770290258291003525915269805681136642429097734000048814298240723562231761619023834394960844029878971981499013038033573258753600902152365790586103706028635244822459196519787862912525491283327007116787602606882496832053009051184570310847543887762939169618346122943625039025871611524626827095753910044498643482695205102890290427695687148069190866692632765483106201399442823616872558241053077599508580374562069324526218753379260063842261274498614487003910139577478888978989041483189689551722095583458639671609218788840529982460497154193249971378228910847994212478043214878031213682100088534428914277243505447832137489008627942471178882980945216262928812975408522454375427823914783986215051147363135783295469065867003204082036556554912428912568587636180093631398457914472659461019002655322144735342322440614063400375174071810698356253926118973729843929653396938907982066217106223980881843978614419692853118550232650818338486530903055079370032411943183284738108999560119575461145508829182462353395906070093585196759980391908985666600348451814825015155488747878738347527346041727342044299408577037099152836762055409372791419875699851565042347462566334258117941524275964818880650403833674052883167426019961349745314739601556867074846910078706737676152434531299559668471581094311585639424598594040719217393851424835053366565101426015370657952050512902974647476624864042816133389770516177011006805321193542307724128710596686853218158081232369581706857623547526054320782098936239671284856295485025979450088187638749023871074549161916864852264418453834897737774060837447213990040146109943664789436392101080735135688083997520674092901098322078922312495410194773428701288131200705452377640475691642896456084993028688323051491736101041861223718727110694266890439587465223726344385874474944452144076185859309024118981831861260141119699541093902384611538394951705576792251435923791982101520738988747224058623234627777104070134524193573566134602098258708784133100563739571392333277440254318445428173619051234089512393825782096572100273493039965267530636460405938796226996519762684308246486406052366933755387375703896365722558237672181556637059447854179257733485280385535912154988325727292452159358778275045763461682709493596424413000349646664794180709942777627744310175936499238998469447852273096548938223996797828734369267065090123223096432644217390681637563623354686717182745330589935006361684189575828473754070281731553512420985502080760633950909458909248123432499066045174698067249226197677892138276954958080867334004478841567618625010698890757857345039695668659171800004122807415411461095899008649344307557116934149393130985721029386608142160429416519638443663145818020960240350804186166619195897377133101836697256906942727205144846559765139392677996750471744733131684312897196841179955667384425382848403166056186318662163341719166626261143097255243559119315141078392263529501650322931781851808804852365555071546720638692712563395081807726827114069551133551278577943813562426952077912740122092875804930274577581028108954538396153896114557290051636711619363231060650804395458526377980137476432778318787965403671614839795194298963176139683493042345559522773836121777922720923383078264562655724673230697436887902994087660242352402489934335853946742649828622132406106568028669990980238594986931918426080234864174428561937991771341919663078851317557918540590182868255310595947372414121930842068578509488757466640544545809603726463948854584100964945390959915958266902424982797241977113516605691287778530131907705265940802980050408491673912387158074217838155235267659758361664517800321866061580377298427365722200283030510204382191829607587442227522068619794579587202581682128679186402132638668044415815177782559999000803710664103986331880231837337849372629410808042534243238824098635066747368268894046915952131310815100466714912093157227157305858946895571170372759364793174957109125233997304176871393080545546020396638610282551375510671336963803688581882872088828063185235355891085854799509819867740889486634191474967870478583925208842801199130284962617509653164390119339268691273947734272882682100026706857812472372354021562808947991088825286925852653254655143087985267929078188830425419833867785650614804925437297847624724393232620239583183180379292594184903522393551973783910667908921131896579321146560734445995851199100152265118895926661136614785434413476677830347326732143396612051375733462206941260656660976966643343734129195245496566146073973253194594393405760779102920612996832513972052159944840021559415502932789229711216021797893978060365840622056066263564423223761724374483532855892450360380243106559647003257282364623097290090675054741707356227426030360760983019798315729059909993187628232111895809528765709776515778133847704374643190646779106944698995157313647884411806579678498260909003463654018860445403540188810617511486670646073570178567385012650496292601173347996336059892423707161778807229467485210807263642579821306383143300460918981178521655173423245995500846280674688695418196063820623754042320943708789559370117496345883074754511788455973806554413478878873569283494125575856598486938310284900274319394970870868133332508961733361698412327954167980496074134175925777417281987690446616643257954122760568110790505351423908969300811420150279526035846268989676587203799190068917429495433014648987422413726108912688534824792336679389066457208737251004350277772189294552924491316700465744244324292691533141144526886675776725909658553673257596898854476542329731250536181615508655163610766351975684401197314314228869115965760795805414846439970263406322328163868310374427737632366175205293401510428861035865389865927694684683109211172568685820592735767173846498617776822756867408589486958780276759534261573500340933525072693280942366364214527430745285781760803814161616778933680265911139329468301546905025238452292521837360471120946961431232628990958641724237904365857577211824058391227168505970063012334637708376182760679604940246769605674805389854971375607006157361923672293525974188249026067861561604104451939522764105944870731149913694412210558324141436076650651354935630818836128751006190895185200846847305959871328088303739317614616656516858176912350278691729239321239499023387709734900103030933314369151806112001274624879269839028769120340908379503211685745251609312535007421922157761206077201773971542504500843933531195656164591281761989463938678786914473007406085119068453410427307979953008108395180847216211834213267498587490445897135072363938492352024300141230968142833822568655831435451883937513850820308097517512398845532876131826831991741712658306685556090536438339296103377123713513994233822326831239375396672672117844170014045646031121164323193983707604685186204366299098310489712854983109928868854428414658927891707798086685697361298196642398131413840134588602466109768745434707931263211277557153826354697170587990468280553127637608889435011705105415861662764423788736907252957849820608539930712292476668397252513673412244619351289599153437758135022365103755792097696176249149478831871680098220658213362940058605033250187326519094899239601133604080404290287698640499789646202985269934073385212413132839072585648591236498411541713630357159241311167008602694431670945871159734979654841772669747271243834131018156325785364340352396082157122175462965597072305671829689728443026593126857545383790781998278637139397013097937446009208751174996874038426266337006101870330897123893750380011546661576620803296093118000999029608680408971846191699379809627967242298238751618110247598666695178463525674585523285081311936286190132331401812275888139076724353384799694159690954071637879711534067237678291496783468240232432133128159334291257795591114378751679285981197979230164461996888686862216836626644079556062675343652808550382278134999009153968144325024362322486223167658899473683088507644379099677478021092324081543066558657680629882057405406802814941930657592941178435975186128128090424170582910614662610330965148130633092879711907957187357828418001227632663796442874970393732692047418749168221429718810933455902070594059139972588245392193450461666246883888935358113367115561005191185753751095650702651884251602783336600810018999009097778367086530928759052152723515722023146825699050258483
0x9e84AAAf...19f49e46A
(Lunchdao: Deployer)
43.567011921463571006 Eth
Nonce: 41
43.544700966575986076 Eth
Nonce: 42
0.02231095488758493
0xee2eBCcB...C24351bfc
(Juicebox: Ticket Booth)

Execution Trace

TicketBooth.issue( _projectId=568, _name=LunchDao, _symbol=LUNCH )
  • Projects.ownerOf( tokenId=568 ) => ( 0x9e84AAAfE42302252A0378d016aE63a19f49e46A )
  • Tickets.61014060( )
    File 1 of 3: TicketBooth
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./interfaces/ITicketBooth.sol";
    import "./abstract/Operatable.sol";
    import "./abstract/TerminalUtility.sol";
    import "./libraries/Operations.sol";
    import "./Tickets.sol";
    /** 
      @notice 
      Manage Ticket printing, redemption, and account balances.
      @dev
      Tickets can be either represented internally staked, or as unstaked ERC-20s.
      This contract manages these two representations and the conversion between the two.
      @dev
      The total supply of a project's tickets and the balance of each account are calculated in this contract.
    */
    contract TicketBooth is TerminalUtility, Operatable, ITicketBooth {
        // --- public immutable stored properties --- //
        /// @notice The Projects contract which mints ERC-721's that represent project ownership and transfers.
        IProjects public immutable override projects;
        // --- public stored properties --- //
        // Each project's ERC20 Ticket tokens.
        mapping(uint256 => ITickets) public override ticketsOf;
        // Each holder's balance of staked Tickets for each project.
        mapping(address => mapping(uint256 => uint256))
            public
            override stakedBalanceOf;
        // The total supply of 1155 tickets for each project.
        mapping(uint256 => uint256) public override stakedTotalSupplyOf;
        // The amount of each holders tickets that are locked.
        mapping(address => mapping(uint256 => uint256))
            public
            override lockedBalanceOf;
        // The amount of each holders tickets that are locked by each address.
        mapping(address => mapping(address => mapping(uint256 => uint256)))
            public
            override lockedBalanceBy;
        // --- external views --- //
        /** 
          @notice 
          The total supply of tickets for each project, including staked and unstaked tickets.
          @param _projectId The ID of the project to get the total supply of.
          @return supply The total supply.
        */
        function totalSupplyOf(uint256 _projectId)
            external
            view
            override
            returns (uint256 supply)
        {
            supply = stakedTotalSupplyOf[_projectId];
            ITickets _tickets = ticketsOf[_projectId];
            if (_tickets != ITickets(address(0)))
                supply = supply + _tickets.totalSupply();
        }
        /** 
          @notice 
          The total balance of tickets a holder has for a specified project, including staked and unstaked tickets.
          @param _holder The ticket holder to get a balance for.
          @param _projectId The project to get the `_hodler`s balance of.
          @return balance The balance.
        */
        function balanceOf(address _holder, uint256 _projectId)
            external
            view
            override
            returns (uint256 balance)
        {
            balance = stakedBalanceOf[_holder][_projectId];
            ITickets _ticket = ticketsOf[_projectId];
            if (_ticket != ITickets(address(0)))
                balance = balance + _ticket.balanceOf(_holder);
        }
        // --- external transactions --- //
        /** 
          @param _projects A Projects contract which mints ERC-721's that represent project ownership and transfers.
          @param _operatorStore A contract storing operator assignments.
          @param _terminalDirectory A directory of a project's current Juicebox terminal to receive payments in.
        */
        constructor(
            IProjects _projects,
            IOperatorStore _operatorStore,
            ITerminalDirectory _terminalDirectory
        ) Operatable(_operatorStore) TerminalUtility(_terminalDirectory) {
            projects = _projects;
        }
        /**
            @notice 
            Issues an owner's ERC-20 Tickets that'll be used when unstaking tickets.
            @dev 
            Deploys an owner's Ticket ERC-20 token contract.
            @param _projectId The ID of the project being issued tickets.
            @param _name The ERC-20's name. " Juicebox ticket" will be appended.
            @param _symbol The ERC-20's symbol. "j" will be prepended.
        */
        function issue(
            uint256 _projectId,
            string calldata _name,
            string calldata _symbol
        )
            external
            override
            requirePermission(
                projects.ownerOf(_projectId),
                _projectId,
                Operations.Issue
            )
        {
            // There must be a name.
            require((bytes(_name).length > 0), "TicketBooth::issue: EMPTY_NAME");
            // There must be a symbol.
            require(
                (bytes(_symbol).length > 0),
                "TicketBooth::issue: EMPTY_SYMBOL"
            );
            // Only one ERC20 ticket can be issued.
            require(
                ticketsOf[_projectId] == ITickets(address(0)),
                "TicketBooth::issue: ALREADY_ISSUED"
            );
            // Create the contract in this TerminalV1 contract in order to have mint and burn privileges.
            // Prepend the strings with standards.
            ticketsOf[_projectId] = new Tickets(_name, _symbol);
            emit Issue(_projectId, _name, _symbol, msg.sender);
        }
        /** 
          @notice 
          Print new tickets.
          @dev
          Only a project's current terminal can print its tickets.
          @param _holder The address receiving the new tickets.
          @param _projectId The project to which the tickets belong.
          @param _amount The amount to print.
          @param _preferUnstakedTickets Whether ERC20's should be converted automatically if they have been issued.
        */
        function print(
            address _holder,
            uint256 _projectId,
            uint256 _amount,
            bool _preferUnstakedTickets
        ) external override onlyTerminal(_projectId) {
            // An amount must be specified.
            require(_amount > 0, "TicketBooth::print: NO_OP");
            // Get a reference to the project's ERC20 tickets.
            ITickets _tickets = ticketsOf[_projectId];
            // If there exists ERC-20 tickets and the caller prefers these unstaked tickets.
            bool _shouldUnstakeTickets = _preferUnstakedTickets &&
                _tickets != ITickets(address(0));
            if (_shouldUnstakeTickets) {
                // Print the equivalent amount of ERC20s.
                _tickets.print(_holder, _amount);
            } else {
                // Add to the staked balance and total supply.
                stakedBalanceOf[_holder][_projectId] =
                    stakedBalanceOf[_holder][_projectId] +
                    _amount;
                stakedTotalSupplyOf[_projectId] =
                    stakedTotalSupplyOf[_projectId] +
                    _amount;
            }
            emit Print(
                _holder,
                _projectId,
                _amount,
                _shouldUnstakeTickets,
                _preferUnstakedTickets,
                msg.sender
            );
        }
        /** 
          @notice 
          Redeems tickets.
          @dev
          Only a project's current terminal can redeem its tickets.
          @param _holder The address that owns the tickets being redeemed.
          @param _projectId The ID of the project of the tickets being redeemed.
          @param _amount The amount of tickets being redeemed.
          @param _preferUnstaked If the preference is to redeem tickets that have been converted to ERC-20s.
        */
        function redeem(
            address _holder,
            uint256 _projectId,
            uint256 _amount,
            bool _preferUnstaked
        ) external override onlyTerminal(_projectId) {
            // Get a reference to the project's ERC20 tickets.
            ITickets _tickets = ticketsOf[_projectId];
            // Get a reference to the staked amount.
            uint256 _unlockedStakedBalance = stakedBalanceOf[_holder][_projectId] -
                lockedBalanceOf[_holder][_projectId];
            // Get a reference to the number of tickets there are.
            uint256 _unstakedBalanceOf = _tickets == ITickets(address(0))
                ? 0
                : _tickets.balanceOf(_holder);
            // There must be enough tickets.
            // Prevent potential overflow by not relying on addition.
            require(
                (_amount < _unstakedBalanceOf &&
                    _amount < _unlockedStakedBalance) ||
                    (_amount >= _unstakedBalanceOf &&
                        _unlockedStakedBalance >= _amount - _unstakedBalanceOf) ||
                    (_amount >= _unlockedStakedBalance &&
                        _unstakedBalanceOf >= _amount - _unlockedStakedBalance),
                "TicketBooth::redeem: INSUFFICIENT_FUNDS"
            );
            // The amount of tickets to redeem.
            uint256 _unstakedTicketsToRedeem;
            // If there's no balance, redeem no tickets
            if (_unstakedBalanceOf == 0) {
                _unstakedTicketsToRedeem = 0;
                // If prefer converted, redeem tickets before redeeming staked tickets.
            } else if (_preferUnstaked) {
                _unstakedTicketsToRedeem = _unstakedBalanceOf >= _amount
                    ? _amount
                    : _unstakedBalanceOf;
                // Otherwise, redeem staked tickets before unstaked tickets.
            } else {
                _unstakedTicketsToRedeem = _unlockedStakedBalance >= _amount
                    ? 0
                    : _amount - _unlockedStakedBalance;
            }
            // The amount of staked tickets to redeem.
            uint256 _stakedTicketsToRedeem = _amount - _unstakedTicketsToRedeem;
            // Redeem the tickets.
            if (_unstakedTicketsToRedeem > 0)
                _tickets.redeem(_holder, _unstakedTicketsToRedeem);
            if (_stakedTicketsToRedeem > 0) {
                // Reduce the holders balance and the total supply.
                stakedBalanceOf[_holder][_projectId] =
                    stakedBalanceOf[_holder][_projectId] -
                    _stakedTicketsToRedeem;
                stakedTotalSupplyOf[_projectId] =
                    stakedTotalSupplyOf[_projectId] -
                    _stakedTicketsToRedeem;
            }
            emit Redeem(
                _holder,
                _projectId,
                _amount,
                _unlockedStakedBalance,
                _preferUnstaked,
                msg.sender
            );
        }
        /**
          @notice 
          Stakes ERC20 tickets by burning their supply and creating an internal staked version.
          @dev
          Only a ticket holder or an operator can stake its tickets.
          @param _holder The owner of the tickets to stake.
          @param _projectId The ID of the project whos tickets are being staked.
          @param _amount The amount of tickets to stake.
         */
        function stake(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        )
            external
            override
            requirePermissionAllowingWildcardDomain(
                _holder,
                _projectId,
                Operations.Stake
            )
        {
            // Get a reference to the project's ERC20 tickets.
            ITickets _tickets = ticketsOf[_projectId];
            // Tickets must have been issued.
            require(
                _tickets != ITickets(address(0)),
                "TicketBooth::stake: NOT_FOUND"
            );
            // Get a reference to the holder's current balance.
            uint256 _unstakedBalanceOf = _tickets.balanceOf(_holder);
            // There must be enough balance to stake.
            require(
                _unstakedBalanceOf >= _amount,
                "TicketBooth::stake: INSUFFICIENT_FUNDS"
            );
            // Redeem the equivalent amount of ERC20s.
            _tickets.redeem(_holder, _amount);
            // Add the staked amount from the holder's balance.
            stakedBalanceOf[_holder][_projectId] =
                stakedBalanceOf[_holder][_projectId] +
                _amount;
            // Add the staked amount from the project's total supply.
            stakedTotalSupplyOf[_projectId] =
                stakedTotalSupplyOf[_projectId] +
                _amount;
            emit Stake(_holder, _projectId, _amount, msg.sender);
        }
        /**
          @notice 
          Unstakes internal tickets by creating and distributing ERC20 tickets.
          @dev
          Only a ticket holder or an operator can unstake its tickets.
          @param _holder The owner of the tickets to unstake.
          @param _projectId The ID of the project whos tickets are being unstaked.
          @param _amount The amount of tickets to unstake.
         */
        function unstake(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        )
            external
            override
            requirePermissionAllowingWildcardDomain(
                _holder,
                _projectId,
                Operations.Unstake
            )
        {
            // Get a reference to the project's ERC20 tickets.
            ITickets _tickets = ticketsOf[_projectId];
            // Tickets must have been issued.
            require(
                _tickets != ITickets(address(0)),
                "TicketBooth::unstake: NOT_FOUND"
            );
            // Get a reference to the amount of unstaked tickets.
            uint256 _unlockedStakedTickets = stakedBalanceOf[_holder][_projectId] -
                lockedBalanceOf[_holder][_projectId];
            // There must be enough unlocked staked tickets to unstake.
            require(
                _unlockedStakedTickets >= _amount,
                "TicketBooth::unstake: INSUFFICIENT_FUNDS"
            );
            // Subtract the unstaked amount from the holder's balance.
            stakedBalanceOf[_holder][_projectId] =
                stakedBalanceOf[_holder][_projectId] -
                _amount;
            // Subtract the unstaked amount from the project's total supply.
            stakedTotalSupplyOf[_projectId] =
                stakedTotalSupplyOf[_projectId] -
                _amount;
            // Print the equivalent amount of ERC20s.
            _tickets.print(_holder, _amount);
            emit Unstake(_holder, _projectId, _amount, msg.sender);
        }
        /** 
          @notice 
          Lock a project's tickets, preventing them from being redeemed and from converting to ERC20s.
          @dev
          Only a ticket holder or an operator can lock its tickets.
          @param _holder The holder to lock tickets from.
          @param _projectId The ID of the project whos tickets are being locked.
          @param _amount The amount of tickets to lock.
        */
        function lock(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        )
            external
            override
            requirePermissionAllowingWildcardDomain(
                _holder,
                _projectId,
                Operations.Lock
            )
        {
            // Amount must be greater than 0.
            require(_amount > 0, "TicketBooth::lock: NO_OP");
            // The holder must have enough tickets to lock.
            require(
                stakedBalanceOf[_holder][_projectId] -
                    lockedBalanceOf[_holder][_projectId] >=
                    _amount,
                "TicketBooth::lock: INSUFFICIENT_FUNDS"
            );
            // Update the lock.
            lockedBalanceOf[_holder][_projectId] =
                lockedBalanceOf[_holder][_projectId] +
                _amount;
            lockedBalanceBy[msg.sender][_holder][_projectId] =
                lockedBalanceBy[msg.sender][_holder][_projectId] +
                _amount;
            emit Lock(_holder, _projectId, _amount, msg.sender);
        }
        /** 
          @notice 
          Unlock a project's tickets.
          @dev
          The address that locked the tickets must be the address that unlocks the tickets.
          @param _holder The holder to unlock tickets from.
          @param _projectId The ID of the project whos tickets are being unlocked.
          @param _amount The amount of tickets to unlock.
        */
        function unlock(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        ) external override {
            // Amount must be greater than 0.
            require(_amount > 0, "TicketBooth::unlock: NO_OP");
            // There must be enough locked tickets to unlock.
            require(
                lockedBalanceBy[msg.sender][_holder][_projectId] >= _amount,
                "TicketBooth::unlock: INSUFFICIENT_FUNDS"
            );
            // Update the lock.
            lockedBalanceOf[_holder][_projectId] =
                lockedBalanceOf[_holder][_projectId] -
                _amount;
            lockedBalanceBy[msg.sender][_holder][_projectId] =
                lockedBalanceBy[msg.sender][_holder][_projectId] -
                _amount;
            emit Unlock(_holder, _projectId, _amount, msg.sender);
        }
        /** 
          @notice 
          Allows a ticket holder to transfer its tickets to another account, without unstaking to ERC-20s.
          @dev
          Only a ticket holder or an operator can transfer its tickets.
          @param _holder The holder to transfer tickets from.
          @param _projectId The ID of the project whos tickets are being transfered.
          @param _amount The amount of tickets to transfer.
          @param _recipient The recipient of the tickets.
        */
        function transfer(
            address _holder,
            uint256 _projectId,
            uint256 _amount,
            address _recipient
        )
            external
            override
            requirePermissionAllowingWildcardDomain(
                _holder,
                _projectId,
                Operations.Transfer
            )
        {
            // Can't transfer to the zero address.
            require(
                _recipient != address(0),
                "TicketBooth::transfer: ZERO_ADDRESS"
            );
            // An address can't transfer to itself.
            require(_holder != _recipient, "TicketBooth::transfer: IDENTITY");
            // There must be an amount to transfer.
            require(_amount > 0, "TicketBooth::transfer: NO_OP");
            // Get a reference to the amount of unlocked staked tickets.
            uint256 _unlockedStakedTickets = stakedBalanceOf[_holder][_projectId] -
                lockedBalanceOf[_holder][_projectId];
            // There must be enough unlocked staked tickets to transfer.
            require(
                _amount <= _unlockedStakedTickets,
                "TicketBooth::transfer: INSUFFICIENT_FUNDS"
            );
            // Subtract from the holder.
            stakedBalanceOf[_holder][_projectId] =
                stakedBalanceOf[_holder][_projectId] -
                _amount;
            // Add the tickets to the recipient.
            stakedBalanceOf[_recipient][_projectId] =
                stakedBalanceOf[_recipient][_projectId] +
                _amount;
            emit Transfer(_holder, _projectId, _recipient, _amount, msg.sender);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./IProjects.sol";
    import "./IOperatorStore.sol";
    import "./ITickets.sol";
    interface ITicketBooth {
        event Issue(
            uint256 indexed projectId,
            string name,
            string symbol,
            address caller
        );
        event Print(
            address indexed holder,
            uint256 indexed projectId,
            uint256 amount,
            bool convertedTickets,
            bool preferUnstakedTickets,
            address controller
        );
        event Redeem(
            address indexed holder,
            uint256 indexed projectId,
            uint256 amount,
            uint256 stakedTickets,
            bool preferUnstaked,
            address controller
        );
        event Stake(
            address indexed holder,
            uint256 indexed projectId,
            uint256 amount,
            address caller
        );
        event Unstake(
            address indexed holder,
            uint256 indexed projectId,
            uint256 amount,
            address caller
        );
        event Lock(
            address indexed holder,
            uint256 indexed projectId,
            uint256 amount,
            address caller
        );
        event Unlock(
            address indexed holder,
            uint256 indexed projectId,
            uint256 amount,
            address caller
        );
        event Transfer(
            address indexed holder,
            uint256 indexed projectId,
            address indexed recipient,
            uint256 amount,
            address caller
        );
        function ticketsOf(uint256 _projectId) external view returns (ITickets);
        function projects() external view returns (IProjects);
        function lockedBalanceOf(address _holder, uint256 _projectId)
            external
            view
            returns (uint256);
        function lockedBalanceBy(
            address _operator,
            address _holder,
            uint256 _projectId
        ) external view returns (uint256);
        function stakedBalanceOf(address _holder, uint256 _projectId)
            external
            view
            returns (uint256);
        function stakedTotalSupplyOf(uint256 _projectId)
            external
            view
            returns (uint256);
        function totalSupplyOf(uint256 _projectId) external view returns (uint256);
        function balanceOf(address _holder, uint256 _projectId)
            external
            view
            returns (uint256 _result);
        function issue(
            uint256 _projectId,
            string calldata _name,
            string calldata _symbol
        ) external;
        function print(
            address _holder,
            uint256 _projectId,
            uint256 _amount,
            bool _preferUnstakedTickets
        ) external;
        function redeem(
            address _holder,
            uint256 _projectId,
            uint256 _amount,
            bool _preferUnstaked
        ) external;
        function stake(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        ) external;
        function unstake(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        ) external;
        function lock(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        ) external;
        function unlock(
            address _holder,
            uint256 _projectId,
            uint256 _amount
        ) external;
        function transfer(
            address _holder,
            uint256 _projectId,
            uint256 _amount,
            address _recipient
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./../interfaces/IOperatable.sol";
    abstract contract Operatable is IOperatable {
        modifier requirePermission(
            address _account,
            uint256 _domain,
            uint256 _index
        ) {
            require(
                msg.sender == _account ||
                    operatorStore.hasPermission(
                        msg.sender,
                        _account,
                        _domain,
                        _index
                    ),
                "Operatable: UNAUTHORIZED"
            );
            _;
        }
        modifier requirePermissionAllowingWildcardDomain(
            address _account,
            uint256 _domain,
            uint256 _index
        ) {
            require(
                msg.sender == _account ||
                    operatorStore.hasPermission(
                        msg.sender,
                        _account,
                        _domain,
                        _index
                    ) ||
                    operatorStore.hasPermission(msg.sender, _account, 0, _index),
                "Operatable: UNAUTHORIZED"
            );
            _;
        }
        modifier requirePermissionAcceptingAlternateAddress(
            address _account,
            uint256 _domain,
            uint256 _index,
            address _alternate
        ) {
            require(
                msg.sender == _account ||
                    operatorStore.hasPermission(
                        msg.sender,
                        _account,
                        _domain,
                        _index
                    ) ||
                    msg.sender == _alternate,
                "Operatable: UNAUTHORIZED"
            );
            _;
        }
        /// @notice A contract storing operator assignments.
        IOperatorStore public immutable override operatorStore;
        /** 
          @param _operatorStore A contract storing operator assignments.
        */
        constructor(IOperatorStore _operatorStore) {
            operatorStore = _operatorStore;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./../interfaces/ITerminalUtility.sol";
    abstract contract TerminalUtility is ITerminalUtility {
        modifier onlyTerminal(uint256 _projectId) {
            require(
                address(terminalDirectory.terminalOf(_projectId)) == msg.sender,
                "TerminalUtility: UNAUTHORIZED"
            );
            _;
        }
        /// @notice The direct deposit terminals.
        ITerminalDirectory public immutable override terminalDirectory;
        /** 
          @param _terminalDirectory A directory of a project's current Juicebox terminal to receive payments in.
        */
        constructor(ITerminalDirectory _terminalDirectory) {
            terminalDirectory = _terminalDirectory;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    library Operations {
        uint256 public constant Configure = 1;
        uint256 public constant PrintPreminedTickets = 2;
        uint256 public constant Redeem = 3;
        uint256 public constant Migrate = 4;
        uint256 public constant SetHandle = 5;
        uint256 public constant SetUri = 6;
        uint256 public constant ClaimHandle = 7;
        uint256 public constant RenewHandle = 8;
        uint256 public constant Issue = 9;
        uint256 public constant Stake = 10;
        uint256 public constant Unstake = 11;
        uint256 public constant Transfer = 12;
        uint256 public constant Lock = 13;
        uint256 public constant SetPayoutMods = 14;
        uint256 public constant SetTicketMods = 15;
        uint256 public constant SetTerminal = 16;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
    import "@paulrberg/contracts/token/erc20/Erc20Permit.sol";
    import "./interfaces/ITickets.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    contract Tickets is ERC20, ERC20Permit, Ownable, ITickets {
        constructor(string memory _name, string memory _symbol)
            ERC20(_name, _symbol)
            ERC20Permit(_name)
        {}
        function print(address _account, uint256 _amount)
            external
            override
            onlyOwner
        {
            return _mint(_account, _amount);
        }
        function redeem(address _account, uint256 _amount)
            external
            override
            onlyOwner
        {
            return _burn(_account, _amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "./ITerminal.sol";
    import "./IOperatorStore.sol";
    interface IProjects is IERC721 {
        event Create(
            uint256 indexed projectId,
            address indexed owner,
            bytes32 indexed handle,
            string uri,
            ITerminal terminal,
            address caller
        );
        event SetHandle(
            uint256 indexed projectId,
            bytes32 indexed handle,
            address caller
        );
        event SetUri(uint256 indexed projectId, string uri, address caller);
        event TransferHandle(
            uint256 indexed projectId,
            address indexed to,
            bytes32 indexed handle,
            bytes32 newHandle,
            address caller
        );
        event ClaimHandle(
            address indexed account,
            uint256 indexed projectId,
            bytes32 indexed handle,
            address caller
        );
        event ChallengeHandle(
            bytes32 indexed handle,
            uint256 challengeExpiry,
            address caller
        );
        event RenewHandle(
            bytes32 indexed handle,
            uint256 indexed projectId,
            address caller
        );
        function count() external view returns (uint256);
        function uriOf(uint256 _projectId) external view returns (string memory);
        function handleOf(uint256 _projectId) external returns (bytes32 handle);
        function projectFor(bytes32 _handle) external returns (uint256 projectId);
        function transferAddressFor(bytes32 _handle)
            external
            returns (address receiver);
        function challengeExpiryOf(bytes32 _handle) external returns (uint256);
        function exists(uint256 _projectId) external view returns (bool);
        function create(
            address _owner,
            bytes32 _handle,
            string calldata _uri,
            ITerminal _terminal
        ) external returns (uint256 id);
        function setHandle(uint256 _projectId, bytes32 _handle) external;
        function setUri(uint256 _projectId, string calldata _uri) external;
        function transferHandle(
            uint256 _projectId,
            address _to,
            bytes32 _newHandle
        ) external returns (bytes32 _handle);
        function claimHandle(
            bytes32 _handle,
            address _for,
            uint256 _projectId
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    interface IOperatorStore {
        event SetOperator(
            address indexed operator,
            address indexed account,
            uint256 indexed domain,
            uint256[] permissionIndexes,
            uint256 packed
        );
        function permissionsOf(
            address _operator,
            address _account,
            uint256 _domain
        ) external view returns (uint256);
        function hasPermission(
            address _operator,
            address _account,
            uint256 _domain,
            uint256 _permissionIndex
        ) external view returns (bool);
        function hasPermissions(
            address _operator,
            address _account,
            uint256 _domain,
            uint256[] calldata _permissionIndexes
        ) external view returns (bool);
        function setOperator(
            address _operator,
            uint256 _domain,
            uint256[] calldata _permissionIndexes
        ) external;
        function setOperators(
            address[] calldata _operators,
            uint256[] calldata _domains,
            uint256[][] calldata _permissionIndexes
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    interface ITickets is IERC20 {
        function print(address _account, uint256 _amount) external;
        function redeem(address _account, uint256 _amount) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./ITerminalDirectory.sol";
    interface ITerminal {
        event Pay(
            uint256 indexed fundingCycleId,
            uint256 indexed projectId,
            address indexed beneficiary,
            uint256 amount,
            string note,
            address caller
        );
        event AddToBalance(
            uint256 indexed projectId,
            uint256 value,
            address caller
        );
        event AllowMigration(ITerminal allowed);
        event Migrate(
            uint256 indexed projectId,
            ITerminal indexed to,
            uint256 _amount,
            address caller
        );
        function terminalDirectory() external view returns (ITerminalDirectory);
        function migrationIsAllowed(ITerminal _terminal)
            external
            view
            returns (bool);
        function pay(
            uint256 _projectId,
            address _beneficiary,
            string calldata _memo,
            bool _preferUnstakedTickets
        ) external payable returns (uint256 fundingCycleId);
        function addToBalance(uint256 _projectId) external payable;
        function allowMigration(ITerminal _contract) external;
        function migrate(uint256 _projectId, ITerminal _to) external;
    }
    // SPDX-License-Identifier: MIT
    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);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./IDirectPaymentAddress.sol";
    import "./ITerminal.sol";
    import "./IProjects.sol";
    import "./IProjects.sol";
    interface ITerminalDirectory {
        event DeployAddress(
            uint256 indexed projectId,
            string memo,
            address indexed caller
        );
        event SetTerminal(
            uint256 indexed projectId,
            ITerminal indexed terminal,
            address caller
        );
        event SetPayerPreferences(
            address indexed account,
            address beneficiary,
            bool preferUnstakedTickets
        );
        function projects() external view returns (IProjects);
        function terminalOf(uint256 _projectId) external view returns (ITerminal);
        function beneficiaryOf(address _account) external returns (address);
        function unstakedTicketsPreferenceOf(address _account)
            external
            returns (bool);
        function addressesOf(uint256 _projectId)
            external
            view
            returns (IDirectPaymentAddress[] memory);
        function deployAddress(uint256 _projectId, string calldata _memo) external;
        function setTerminal(uint256 _projectId, ITerminal _terminal) external;
        function setPayerPreferences(
            address _beneficiary,
            bool _preferUnstakedTickets
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./ITerminalDirectory.sol";
    import "./ITerminal.sol";
    interface IDirectPaymentAddress {
        event Forward(
            address indexed payer,
            uint256 indexed projectId,
            address beneficiary,
            uint256 value,
            string memo,
            bool preferUnstakedTickets
        );
        function terminalDirectory() external returns (ITerminalDirectory);
        function projectId() external returns (uint256);
        function memo() external returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./IOperatorStore.sol";
    interface IOperatable {
        function operatorStore() external view returns (IOperatorStore);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./ITerminalDirectory.sol";
    interface ITerminalUtility {
        function terminalDirectory() external view returns (ITerminalDirectory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * The default value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overridden;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * Requirements:
         *
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            uint256 currentAllowance = _allowances[sender][_msgSender()];
            require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
            unchecked {
                _approve(sender, _msgSender(), currentAllowance - amount);
            }
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            uint256 currentAllowance = _allowances[_msgSender()][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(_msgSender(), spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `sender` to `recipient`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            uint256 senderBalance = _balances[sender];
            require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[sender] = senderBalance - amount;
            }
            _balances[recipient] += amount;
            emit Transfer(sender, recipient, amount);
            _afterTokenTransfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
            }
            _totalSupply -= amount;
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./draft-IERC20Permit.sol";
    import "../ERC20.sol";
    import "../../../utils/cryptography/draft-EIP712.sol";
    import "../../../utils/cryptography/ECDSA.sol";
    import "../../../utils/Counters.sol";
    /**
     * @dev Implementation 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.
     *
     * _Available since v3.4._
     */
    abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
        using Counters for Counters.Counter;
        mapping(address => Counters.Counter) private _nonces;
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private immutable _PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        /**
         * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
         *
         * It's a good idea to use the same `name` that is defined as the ERC20 token name.
         */
        constructor(string memory name) EIP712(name, "1") {}
        /**
         * @dev See {IERC20Permit-permit}.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual override {
            require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
            bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
            bytes32 hash = _hashTypedDataV4(structHash);
            address signer = ECDSA.recover(hash, v, r, s);
            require(signer == owner, "ERC20Permit: invalid signature");
            _approve(owner, spender, value);
        }
        /**
         * @dev See {IERC20Permit-nonces}.
         */
        function nonces(address owner) public view virtual override returns (uint256) {
            return _nonces[owner].current();
        }
        /**
         * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view override returns (bytes32) {
            return _domainSeparatorV4();
        }
        /**
         * @dev "Consume a nonce": return the current value and increment.
         *
         * _Available since v4.1._
         */
        function _useNonce(address owner) internal virtual returns (uint256 current) {
            Counters.Counter storage nonce = _nonces[owner];
            current = nonce.current();
            nonce.increment();
        }
    }
    // SPDX-License-Identifier: WTFPL
    // solhint-disable var-name-mixedcase
    pragma solidity >=0.8.4;
    import "./Erc20.sol";
    import "./IErc20Permit.sol";
    /// @notice Emitted when the recovered owner does not match the actual owner.
    error Erc20Permit__InvalidSignature(uint8 v, bytes32 r, bytes32 s);
    /// @notice Emitted when the owner is the zero address.
    error Erc20Permit__OwnerZeroAddress();
    /// @notice Emitted when the permit expired.
    error Erc20Permit__PermitExpired(uint256 deadline);
    /// @notice Emitted when the recovered owner is the zero address.
    error Erc20Permit__RecoveredOwnerZeroAddress();
    /// @notice Emitted when the spender is the zero address.
    error Erc20Permit__SpenderZeroAddress();
    /// @title Erc20Permit
    /// @author Paul Razvan Berg
    contract Erc20Permit is
        IErc20Permit, // one dependency
        Erc20 // one dependency
    {
        /// PUBLIC STORAGE ///
        /// @inheritdoc IErc20Permit
        bytes32 public immutable override DOMAIN_SEPARATOR;
        /// @inheritdoc IErc20Permit
        bytes32 public constant override PERMIT_TYPEHASH =
            0xfc77c2b9d30fe91687fd39abb7d16fcdfe1472d065740051ab8b13e4bf4a617f;
        /// @inheritdoc IErc20Permit
        mapping(address => uint256) public override nonces;
        /// @inheritdoc IErc20Permit
        string public constant override version = "1";
        /// CONSTRUCTOR ///
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) Erc20(_name, _symbol, _decimals) {
            uint256 chainId;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                chainId := chainid()
            }
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256(bytes(version)),
                    chainId,
                    address(this)
                )
            );
        }
        /// PUBLIC NON-CONSTANT FUNCTIONS ///
        /// @inheritdoc IErc20Permit
        function permit(
            address owner,
            address spender,
            uint256 amount,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external override {
            if (owner == address(0)) {
                revert Erc20Permit__OwnerZeroAddress();
            }
            if (spender == address(0)) {
                revert Erc20Permit__SpenderZeroAddress();
            }
            if (deadline < block.timestamp) {
                revert Erc20Permit__PermitExpired(deadline);
            }
            // It's safe to use the "+" operator here because the nonce cannot realistically overflow, ever.
            bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, nonces[owner]++, deadline));
            bytes32 digest = keccak256(abi.encodePacked("\\x19\\x01", DOMAIN_SEPARATOR, hashStruct));
            address recoveredOwner = ecrecover(digest, v, r, s);
            if (recoveredOwner == address(0)) {
                revert Erc20Permit__RecoveredOwnerZeroAddress();
            }
            if (recoveredOwner != owner) {
                revert Erc20Permit__InvalidSignature(v, r, s);
            }
            approveInternal(owner, spender, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    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() {
            _setOwner(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _setOwner(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");
            _setOwner(newOwner);
        }
        function _setOwner(address newOwner) private {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    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.8.0;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./ECDSA.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256(
                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
            );
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _TYPE_HASH = typeHash;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
        function _buildDomainSeparator(
            bytes32 typeHash,
            bytes32 nameHash,
            bytes32 versionHash
        ) private view returns (bytes32) {
            return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            // Check the signature length
            // - case 65: r,s,v signature (standard)
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return recover(hash, v, r, s);
            } else if (signature.length == 64) {
                bytes32 r;
                bytes32 vs;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    vs := mload(add(signature, 0x40))
                }
                return recover(hash, r, vs);
            } else {
                revert("ECDSA: invalid signature length");
            }
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            bytes32 s;
            uint8 v;
            assembly {
                s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                v := add(shr(255, vs), 27)
            }
            return recover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`, `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            require(
                uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                "ECDSA: invalid signature 's' value"
            );
            require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            require(signer != address(0), "ECDSA: invalid signature");
            return signer;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    // SPDX-License-Identifier: WTFPL
    pragma solidity >=0.8.4;
    import "./IErc20.sol";
    /// @notice Emitted when the owner is the zero address.
    error Erc20__ApproveOwnerZeroAddress();
    /// @notice Emitted when the spender is the zero address.
    error Erc20__ApproveSpenderZeroAddress();
    /// @notice Emitted when burning more tokens than are in the account.
    error Erc20__BurnUnderflow(uint256 accountBalance, uint256 burnAmount);
    /// @notice Emitted when the holder is the zero address.
    error Erc20__BurnZeroAddress();
    /// @notice Emitted when the sender did not give the caller a sufficient allowance.
    error Erc20__InsufficientAllowance(uint256 allowance, uint256 amount);
    /// @notice Emitted when the beneficiary is the zero address.
    error Erc20__MintZeroAddress();
    /// @notice Emitted when tranferring more tokens than there are in the account.
    error Erc20__TransferUnderflow(uint256 senderBalance, uint256 amount);
    /// @notice Emitted when the sender is the zero address.
    error Erc20__TransferSenderZeroAddress();
    /// @notice Emitted when the recipient is the zero address.
    error Erc20__TransferRecipientZeroAddress();
    /// @title Erc20
    /// @author Paul Razvan Berg
    contract Erc20 is IErc20 {
        /// PUBLIC STORAGE ///
        /// @inheritdoc IErc20
        string public override name;
        /// @inheritdoc IErc20
        string public override symbol;
        /// @inheritdoc IErc20
        uint8 public immutable override decimals;
        /// @inheritdoc IErc20
        uint256 public override totalSupply;
        /// @inheritdoc IErc20
        mapping(address => uint256) public override balanceOf;
        /// @inheritdoc IErc20
        mapping(address => mapping(address => uint256)) public override allowance;
        /// CONSTRUCTOR ///
        /// @notice All three of these values are immutable: they can only be set once during construction.
        /// @param _name Erc20 name of this token.
        /// @param _symbol Erc20 symbol of this token.
        /// @param _decimals Erc20 decimal precision of this token.
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
        }
        /// PUBLIC NON-CONSTANT FUNCTIONS ///
        /// @inheritdoc IErc20
        function approve(address spender, uint256 amount) external virtual override returns (bool) {
            approveInternal(msg.sender, spender, amount);
            return true;
        }
        /// @inheritdoc IErc20
        function decreaseAllowance(address spender, uint256 subtractedValue) external virtual override returns (bool) {
            uint256 newAllowance = allowance[msg.sender][spender] - subtractedValue;
            approveInternal(msg.sender, spender, newAllowance);
            return true;
        }
        /// @inheritdoc IErc20
        function increaseAllowance(address spender, uint256 addedValue) external virtual override returns (bool) {
            uint256 newAllowance = allowance[msg.sender][spender] + addedValue;
            approveInternal(msg.sender, spender, newAllowance);
            return true;
        }
        /// @inheritdoc IErc20
        function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
            transferInternal(msg.sender, recipient, amount);
            return true;
        }
        /// @inheritdoc IErc20
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external virtual override returns (bool) {
            transferInternal(sender, recipient, amount);
            uint256 currentAllowance = allowance[sender][msg.sender];
            if (currentAllowance < amount) {
                revert Erc20__InsufficientAllowance(currentAllowance, amount);
            }
            approveInternal(sender, msg.sender, currentAllowance);
            return true;
        }
        /// INTERNAL NON-CONSTANT FUNCTIONS ///
        /// @notice Sets `amount` as the allowance of `spender` over the `owner`s tokens.
        ///
        /// @dev Emits an {Approval} event.
        ///
        /// This is internal function is equivalent to `approve`, and can be used to e.g. set automatic
        /// allowances for certain subsystems, etc.
        ///
        /// Requirements:
        ///
        /// - `owner` cannot be the zero address.
        /// - `spender` cannot be the zero address.
        function approveInternal(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            if (owner == address(0)) {
                revert Erc20__ApproveOwnerZeroAddress();
            }
            if (spender == address(0)) {
                revert Erc20__ApproveSpenderZeroAddress();
            }
            allowance[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /// @notice Destroys `burnAmount` tokens from `holder`, reducing the token supply.
        ///
        /// @dev Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - `holder` must have at least `amount` tokens.
        function burnInternal(address holder, uint256 burnAmount) internal {
            if (holder == address(0)) {
                revert Erc20__BurnZeroAddress();
            }
            uint256 accountBalance = balanceOf[holder];
            if (accountBalance < burnAmount) {
                revert Erc20__BurnUnderflow(accountBalance, burnAmount);
            }
            // Burn the tokens.
            unchecked {
                balanceOf[holder] = accountBalance - burnAmount;
            }
            // Reduce the total supply.
            totalSupply -= burnAmount;
            emit Transfer(holder, address(0), burnAmount);
        }
        /// @notice Prints new tokens into existence and assigns them to `beneficiary`, increasing the
        /// total supply.
        ///
        /// @dev Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - The beneficiary's balance and the total supply cannot overflow.
        function mintInternal(address beneficiary, uint256 mintAmount) internal {
            if (beneficiary == address(0)) {
                revert Erc20__MintZeroAddress();
            }
            /// Mint the new tokens.
            balanceOf[beneficiary] += mintAmount;
            /// Increase the total supply.
            totalSupply += mintAmount;
            emit Transfer(address(0), beneficiary, mintAmount);
        }
        /// @notice Moves `amount` tokens from `sender` to `recipient`.
        ///
        /// @dev This is internal function is equivalent to {transfer}, and can be used to e.g. implement
        /// automatic token fees, slashing mechanisms, etc.
        ///
        /// Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - `sender` cannot be the zero address.
        /// - `recipient` cannot be the zero address.
        /// - `sender` must have a balance of at least `amount`.
        function transferInternal(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            if (sender == address(0)) {
                revert Erc20__TransferSenderZeroAddress();
            }
            if (recipient == address(0)) {
                revert Erc20__TransferRecipientZeroAddress();
            }
            uint256 senderBalance = balanceOf[sender];
            if (senderBalance < amount) {
                revert Erc20__TransferUnderflow(senderBalance, amount);
            }
            unchecked {
                balanceOf[sender] = senderBalance - amount;
            }
            balanceOf[recipient] += amount;
            emit Transfer(sender, recipient, amount);
        }
    }
    // SPDX-License-Identifier: WTFPL
    // solhint-disable func-name-mixedcase
    pragma solidity >=0.8.4;
    import "./IErc20.sol";
    /// @title IErc20Permit
    /// @author Paul Razvan Berg
    /// @notice Extension of Erc20 that allows token holders to use their tokens without sending any
    /// transactions by setting the allowance with a signature using the `permit` method, and then spend
    /// them via `transferFrom`.
    /// @dev See https://eips.ethereum.org/EIPS/eip-2612.
    interface IErc20Permit is IErc20 {
        /// NON-CONSTANT FUNCTIONS ///
        /// @notice Sets `amount` as the allowance of `spender` over `owner`'s tokens, assuming the latter's
        /// signed approval.
        ///
        /// @dev Emits an {Approval} event.
        ///
        /// IMPORTANT: The same issues Erc20 `approve` has related to transaction
        /// ordering also apply here.
        ///
        /// Requirements:
        ///
        /// - `owner` cannot be the zero address.
        /// - `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.
        function permit(
            address owner,
            address spender,
            uint256 amount,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /// CONSTANT FUNCTIONS ///
        /// @notice The Eip712 domain's keccak256 hash.
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        /// @notice Provides replay protection.
        function nonces(address account) external view returns (uint256);
        /// @notice keccak256("Permit(address owner,address spender,uint256 amount,uint256 nonce,uint256 deadline)");
        function PERMIT_TYPEHASH() external view returns (bytes32);
        /// @notice Eip712 version of this implementation.
        function version() external view returns (string memory);
    }
    // SPDX-License-Identifier: WTFPL
    pragma solidity >=0.8.4;
    /// @title IErc20
    /// @author Paul Razvan Berg
    /// @notice Implementation for the Erc20 standard.
    ///
    /// We have followed general OpenZeppelin guidelines: functions revert instead of returning
    /// `false` on failure. This behavior is nonetheless conventional and does not conflict with
    /// the with the expectations of Erc20 applications.
    ///
    /// Additionally, an {Approval} event is emitted on calls to {transferFrom}. This allows
    /// applications to reconstruct the allowance for all accounts just by listening to said
    /// events. Other implementations of the Erc may not emit these events, as it isn't
    /// required by the specification.
    ///
    /// Finally, the non-standard {decreaseAllowance} and {increaseAllowance} functions have been
    /// added to mitigate the well-known issues around setting allowances.
    ///
    /// @dev Forked from OpenZeppelin
    /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC20/ERC20.sol
    interface IErc20 {
        /// EVENTS ///
        /// @notice Emitted when an approval happens.
        /// @param owner The address of the owner of the tokens.
        /// @param spender The address of the spender.
        /// @param amount The maximum amount that can be spent.
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /// @notice Emitted when a transfer happens.
        /// @param from The account sending the tokens.
        /// @param to The account receiving the tokens.
        /// @param amount The amount of tokens transferred.
        event Transfer(address indexed from, address indexed to, uint256 amount);
        /// CONSTANT FUNCTIONS ///
        /// @notice Returns the remaining number of tokens that `spender` will be allowed to spend
        /// on behalf of `owner` through {transferFrom}. This is zero by default.
        ///
        /// @dev This value changes when {approve} or {transferFrom} are called.
        function allowance(address owner, address spender) external view returns (uint256);
        /// @notice Returns the amount of tokens owned by `account`.
        function balanceOf(address account) external view returns (uint256);
        /// @notice Returns the number of decimals used to get its user representation.
        function decimals() external view returns (uint8);
        /// @notice Returns the name of the token.
        function name() external view returns (string memory);
        /// @notice Returns the symbol of the token, usually a shorter version of the name.
        function symbol() external view returns (string memory);
        /// @notice Returns the amount of tokens in existence.
        function totalSupply() external view returns (uint256);
        /// NON-CONSTANT FUNCTIONS ///
        /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
        ///
        /// @dev Emits an {Approval} event.
        ///
        /// 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
        ///
        /// Requirements:
        ///
        /// - `spender` cannot be the zero address.
        ///
        /// @return a boolean value indicating whether the operation succeeded.
        function approve(address spender, uint256 amount) external returns (bool);
        /// @notice Atomically decreases the allowance granted to `spender` by the caller.
        ///
        /// @dev Emits an {Approval} event indicating the updated allowance.
        ///
        /// This is an alternative to {approve} that can be used as a mitigation for problems described
        /// in {Erc20Interface-approve}.
        ///
        /// Requirements:
        ///
        /// - `spender` cannot be the zero address.
        /// - `spender` must have allowance for the caller of at least `subtractedValue`.
        function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
        /// @notice Atomically increases the allowance granted to `spender` by the caller.
        ///
        /// @dev Emits an {Approval} event indicating the updated allowance.
        ///
        /// This is an alternative to {approve} that can be used as a mitigation for the problems described above.
        ///
        /// Requirements:
        ///
        /// - `spender` cannot be the zero address.
        function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
        /// @notice Moves `amount` tokens from the caller's account to `recipient`.
        ///
        /// @dev Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - `recipient` cannot be the zero address.
        /// - The caller must have a balance of at least `amount`.
        ///
        /// @return a boolean value indicating whether the operation succeeded.
        function transfer(address recipient, uint256 amount) external returns (bool);
        /// @notice Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount`
        /// `is then deducted from the caller's allowance.
        ///
        /// @dev Emits a {Transfer} event and an {Approval} event indicating the updated allowance. This is
        /// not required by the Erc. See the note at the beginning of {Erc20}.
        ///
        /// Requirements:
        ///
        /// - `sender` and `recipient` cannot be the zero address.
        /// - `sender` must have a balance of at least `amount`.
        /// - The caller must have approed `sender` to spent at least `amount` tokens.
        ///
        /// @return a boolean value indicating whether the operation succeeded.
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
    }
    

    File 2 of 3: Tickets
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
    import "@paulrberg/contracts/token/erc20/Erc20Permit.sol";
    import "./interfaces/ITickets.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    contract Tickets is ERC20, ERC20Permit, Ownable, ITickets {
        constructor(string memory _name, string memory _symbol)
            ERC20(_name, _symbol)
            ERC20Permit(_name)
        {}
        function print(address _account, uint256 _amount)
            external
            override
            onlyOwner
        {
            return _mint(_account, _amount);
        }
        function redeem(address _account, uint256 _amount)
            external
            override
            onlyOwner
        {
            return _burn(_account, _amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * The default value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overridden;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * Requirements:
         *
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            uint256 currentAllowance = _allowances[sender][_msgSender()];
            require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
            unchecked {
                _approve(sender, _msgSender(), currentAllowance - amount);
            }
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            uint256 currentAllowance = _allowances[_msgSender()][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(_msgSender(), spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `sender` to `recipient`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            uint256 senderBalance = _balances[sender];
            require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[sender] = senderBalance - amount;
            }
            _balances[recipient] += amount;
            emit Transfer(sender, recipient, amount);
            _afterTokenTransfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
            }
            _totalSupply -= amount;
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./draft-IERC20Permit.sol";
    import "../ERC20.sol";
    import "../../../utils/cryptography/draft-EIP712.sol";
    import "../../../utils/cryptography/ECDSA.sol";
    import "../../../utils/Counters.sol";
    /**
     * @dev Implementation 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.
     *
     * _Available since v3.4._
     */
    abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
        using Counters for Counters.Counter;
        mapping(address => Counters.Counter) private _nonces;
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private immutable _PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        /**
         * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
         *
         * It's a good idea to use the same `name` that is defined as the ERC20 token name.
         */
        constructor(string memory name) EIP712(name, "1") {}
        /**
         * @dev See {IERC20Permit-permit}.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual override {
            require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
            bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
            bytes32 hash = _hashTypedDataV4(structHash);
            address signer = ECDSA.recover(hash, v, r, s);
            require(signer == owner, "ERC20Permit: invalid signature");
            _approve(owner, spender, value);
        }
        /**
         * @dev See {IERC20Permit-nonces}.
         */
        function nonces(address owner) public view virtual override returns (uint256) {
            return _nonces[owner].current();
        }
        /**
         * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view override returns (bytes32) {
            return _domainSeparatorV4();
        }
        /**
         * @dev "Consume a nonce": return the current value and increment.
         *
         * _Available since v4.1._
         */
        function _useNonce(address owner) internal virtual returns (uint256 current) {
            Counters.Counter storage nonce = _nonces[owner];
            current = nonce.current();
            nonce.increment();
        }
    }
    // SPDX-License-Identifier: WTFPL
    // solhint-disable var-name-mixedcase
    pragma solidity >=0.8.4;
    import "./Erc20.sol";
    import "./IErc20Permit.sol";
    /// @notice Emitted when the recovered owner does not match the actual owner.
    error Erc20Permit__InvalidSignature(uint8 v, bytes32 r, bytes32 s);
    /// @notice Emitted when the owner is the zero address.
    error Erc20Permit__OwnerZeroAddress();
    /// @notice Emitted when the permit expired.
    error Erc20Permit__PermitExpired(uint256 deadline);
    /// @notice Emitted when the recovered owner is the zero address.
    error Erc20Permit__RecoveredOwnerZeroAddress();
    /// @notice Emitted when the spender is the zero address.
    error Erc20Permit__SpenderZeroAddress();
    /// @title Erc20Permit
    /// @author Paul Razvan Berg
    contract Erc20Permit is
        IErc20Permit, // one dependency
        Erc20 // one dependency
    {
        /// PUBLIC STORAGE ///
        /// @inheritdoc IErc20Permit
        bytes32 public immutable override DOMAIN_SEPARATOR;
        /// @inheritdoc IErc20Permit
        bytes32 public constant override PERMIT_TYPEHASH =
            0xfc77c2b9d30fe91687fd39abb7d16fcdfe1472d065740051ab8b13e4bf4a617f;
        /// @inheritdoc IErc20Permit
        mapping(address => uint256) public override nonces;
        /// @inheritdoc IErc20Permit
        string public constant override version = "1";
        /// CONSTRUCTOR ///
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) Erc20(_name, _symbol, _decimals) {
            uint256 chainId;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                chainId := chainid()
            }
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256(bytes(version)),
                    chainId,
                    address(this)
                )
            );
        }
        /// PUBLIC NON-CONSTANT FUNCTIONS ///
        /// @inheritdoc IErc20Permit
        function permit(
            address owner,
            address spender,
            uint256 amount,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external override {
            if (owner == address(0)) {
                revert Erc20Permit__OwnerZeroAddress();
            }
            if (spender == address(0)) {
                revert Erc20Permit__SpenderZeroAddress();
            }
            if (deadline < block.timestamp) {
                revert Erc20Permit__PermitExpired(deadline);
            }
            // It's safe to use the "+" operator here because the nonce cannot realistically overflow, ever.
            bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, nonces[owner]++, deadline));
            bytes32 digest = keccak256(abi.encodePacked("\\x19\\x01", DOMAIN_SEPARATOR, hashStruct));
            address recoveredOwner = ecrecover(digest, v, r, s);
            if (recoveredOwner == address(0)) {
                revert Erc20Permit__RecoveredOwnerZeroAddress();
            }
            if (recoveredOwner != owner) {
                revert Erc20Permit__InvalidSignature(v, r, s);
            }
            approveInternal(owner, spender, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    interface ITickets is IERC20 {
        function print(address _account, uint256 _amount) external;
        function redeem(address _account, uint256 _amount) external;
    }
    // SPDX-License-Identifier: MIT
    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() {
            _setOwner(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _setOwner(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");
            _setOwner(newOwner);
        }
        function _setOwner(address newOwner) private {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    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.8.0;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./ECDSA.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256(
                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
            );
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _TYPE_HASH = typeHash;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
        function _buildDomainSeparator(
            bytes32 typeHash,
            bytes32 nameHash,
            bytes32 versionHash
        ) private view returns (bytes32) {
            return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            // Check the signature length
            // - case 65: r,s,v signature (standard)
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return recover(hash, v, r, s);
            } else if (signature.length == 64) {
                bytes32 r;
                bytes32 vs;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    vs := mload(add(signature, 0x40))
                }
                return recover(hash, r, vs);
            } else {
                revert("ECDSA: invalid signature length");
            }
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            bytes32 s;
            uint8 v;
            assembly {
                s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                v := add(shr(255, vs), 27)
            }
            return recover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`, `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            require(
                uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                "ECDSA: invalid signature 's' value"
            );
            require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            require(signer != address(0), "ECDSA: invalid signature");
            return signer;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    // SPDX-License-Identifier: WTFPL
    pragma solidity >=0.8.4;
    import "./IErc20.sol";
    /// @notice Emitted when the owner is the zero address.
    error Erc20__ApproveOwnerZeroAddress();
    /// @notice Emitted when the spender is the zero address.
    error Erc20__ApproveSpenderZeroAddress();
    /// @notice Emitted when burning more tokens than are in the account.
    error Erc20__BurnUnderflow(uint256 accountBalance, uint256 burnAmount);
    /// @notice Emitted when the holder is the zero address.
    error Erc20__BurnZeroAddress();
    /// @notice Emitted when the sender did not give the caller a sufficient allowance.
    error Erc20__InsufficientAllowance(uint256 allowance, uint256 amount);
    /// @notice Emitted when the beneficiary is the zero address.
    error Erc20__MintZeroAddress();
    /// @notice Emitted when tranferring more tokens than there are in the account.
    error Erc20__TransferUnderflow(uint256 senderBalance, uint256 amount);
    /// @notice Emitted when the sender is the zero address.
    error Erc20__TransferSenderZeroAddress();
    /// @notice Emitted when the recipient is the zero address.
    error Erc20__TransferRecipientZeroAddress();
    /// @title Erc20
    /// @author Paul Razvan Berg
    contract Erc20 is IErc20 {
        /// PUBLIC STORAGE ///
        /// @inheritdoc IErc20
        string public override name;
        /// @inheritdoc IErc20
        string public override symbol;
        /// @inheritdoc IErc20
        uint8 public immutable override decimals;
        /// @inheritdoc IErc20
        uint256 public override totalSupply;
        /// @inheritdoc IErc20
        mapping(address => uint256) public override balanceOf;
        /// @inheritdoc IErc20
        mapping(address => mapping(address => uint256)) public override allowance;
        /// CONSTRUCTOR ///
        /// @notice All three of these values are immutable: they can only be set once during construction.
        /// @param _name Erc20 name of this token.
        /// @param _symbol Erc20 symbol of this token.
        /// @param _decimals Erc20 decimal precision of this token.
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
        }
        /// PUBLIC NON-CONSTANT FUNCTIONS ///
        /// @inheritdoc IErc20
        function approve(address spender, uint256 amount) external virtual override returns (bool) {
            approveInternal(msg.sender, spender, amount);
            return true;
        }
        /// @inheritdoc IErc20
        function decreaseAllowance(address spender, uint256 subtractedValue) external virtual override returns (bool) {
            uint256 newAllowance = allowance[msg.sender][spender] - subtractedValue;
            approveInternal(msg.sender, spender, newAllowance);
            return true;
        }
        /// @inheritdoc IErc20
        function increaseAllowance(address spender, uint256 addedValue) external virtual override returns (bool) {
            uint256 newAllowance = allowance[msg.sender][spender] + addedValue;
            approveInternal(msg.sender, spender, newAllowance);
            return true;
        }
        /// @inheritdoc IErc20
        function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
            transferInternal(msg.sender, recipient, amount);
            return true;
        }
        /// @inheritdoc IErc20
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external virtual override returns (bool) {
            transferInternal(sender, recipient, amount);
            uint256 currentAllowance = allowance[sender][msg.sender];
            if (currentAllowance < amount) {
                revert Erc20__InsufficientAllowance(currentAllowance, amount);
            }
            approveInternal(sender, msg.sender, currentAllowance);
            return true;
        }
        /// INTERNAL NON-CONSTANT FUNCTIONS ///
        /// @notice Sets `amount` as the allowance of `spender` over the `owner`s tokens.
        ///
        /// @dev Emits an {Approval} event.
        ///
        /// This is internal function is equivalent to `approve`, and can be used to e.g. set automatic
        /// allowances for certain subsystems, etc.
        ///
        /// Requirements:
        ///
        /// - `owner` cannot be the zero address.
        /// - `spender` cannot be the zero address.
        function approveInternal(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            if (owner == address(0)) {
                revert Erc20__ApproveOwnerZeroAddress();
            }
            if (spender == address(0)) {
                revert Erc20__ApproveSpenderZeroAddress();
            }
            allowance[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /// @notice Destroys `burnAmount` tokens from `holder`, reducing the token supply.
        ///
        /// @dev Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - `holder` must have at least `amount` tokens.
        function burnInternal(address holder, uint256 burnAmount) internal {
            if (holder == address(0)) {
                revert Erc20__BurnZeroAddress();
            }
            uint256 accountBalance = balanceOf[holder];
            if (accountBalance < burnAmount) {
                revert Erc20__BurnUnderflow(accountBalance, burnAmount);
            }
            // Burn the tokens.
            unchecked {
                balanceOf[holder] = accountBalance - burnAmount;
            }
            // Reduce the total supply.
            totalSupply -= burnAmount;
            emit Transfer(holder, address(0), burnAmount);
        }
        /// @notice Prints new tokens into existence and assigns them to `beneficiary`, increasing the
        /// total supply.
        ///
        /// @dev Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - The beneficiary's balance and the total supply cannot overflow.
        function mintInternal(address beneficiary, uint256 mintAmount) internal {
            if (beneficiary == address(0)) {
                revert Erc20__MintZeroAddress();
            }
            /// Mint the new tokens.
            balanceOf[beneficiary] += mintAmount;
            /// Increase the total supply.
            totalSupply += mintAmount;
            emit Transfer(address(0), beneficiary, mintAmount);
        }
        /// @notice Moves `amount` tokens from `sender` to `recipient`.
        ///
        /// @dev This is internal function is equivalent to {transfer}, and can be used to e.g. implement
        /// automatic token fees, slashing mechanisms, etc.
        ///
        /// Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - `sender` cannot be the zero address.
        /// - `recipient` cannot be the zero address.
        /// - `sender` must have a balance of at least `amount`.
        function transferInternal(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            if (sender == address(0)) {
                revert Erc20__TransferSenderZeroAddress();
            }
            if (recipient == address(0)) {
                revert Erc20__TransferRecipientZeroAddress();
            }
            uint256 senderBalance = balanceOf[sender];
            if (senderBalance < amount) {
                revert Erc20__TransferUnderflow(senderBalance, amount);
            }
            unchecked {
                balanceOf[sender] = senderBalance - amount;
            }
            balanceOf[recipient] += amount;
            emit Transfer(sender, recipient, amount);
        }
    }
    // SPDX-License-Identifier: WTFPL
    // solhint-disable func-name-mixedcase
    pragma solidity >=0.8.4;
    import "./IErc20.sol";
    /// @title IErc20Permit
    /// @author Paul Razvan Berg
    /// @notice Extension of Erc20 that allows token holders to use their tokens without sending any
    /// transactions by setting the allowance with a signature using the `permit` method, and then spend
    /// them via `transferFrom`.
    /// @dev See https://eips.ethereum.org/EIPS/eip-2612.
    interface IErc20Permit is IErc20 {
        /// NON-CONSTANT FUNCTIONS ///
        /// @notice Sets `amount` as the allowance of `spender` over `owner`'s tokens, assuming the latter's
        /// signed approval.
        ///
        /// @dev Emits an {Approval} event.
        ///
        /// IMPORTANT: The same issues Erc20 `approve` has related to transaction
        /// ordering also apply here.
        ///
        /// Requirements:
        ///
        /// - `owner` cannot be the zero address.
        /// - `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.
        function permit(
            address owner,
            address spender,
            uint256 amount,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /// CONSTANT FUNCTIONS ///
        /// @notice The Eip712 domain's keccak256 hash.
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        /// @notice Provides replay protection.
        function nonces(address account) external view returns (uint256);
        /// @notice keccak256("Permit(address owner,address spender,uint256 amount,uint256 nonce,uint256 deadline)");
        function PERMIT_TYPEHASH() external view returns (bytes32);
        /// @notice Eip712 version of this implementation.
        function version() external view returns (string memory);
    }
    // SPDX-License-Identifier: WTFPL
    pragma solidity >=0.8.4;
    /// @title IErc20
    /// @author Paul Razvan Berg
    /// @notice Implementation for the Erc20 standard.
    ///
    /// We have followed general OpenZeppelin guidelines: functions revert instead of returning
    /// `false` on failure. This behavior is nonetheless conventional and does not conflict with
    /// the with the expectations of Erc20 applications.
    ///
    /// Additionally, an {Approval} event is emitted on calls to {transferFrom}. This allows
    /// applications to reconstruct the allowance for all accounts just by listening to said
    /// events. Other implementations of the Erc may not emit these events, as it isn't
    /// required by the specification.
    ///
    /// Finally, the non-standard {decreaseAllowance} and {increaseAllowance} functions have been
    /// added to mitigate the well-known issues around setting allowances.
    ///
    /// @dev Forked from OpenZeppelin
    /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC20/ERC20.sol
    interface IErc20 {
        /// EVENTS ///
        /// @notice Emitted when an approval happens.
        /// @param owner The address of the owner of the tokens.
        /// @param spender The address of the spender.
        /// @param amount The maximum amount that can be spent.
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /// @notice Emitted when a transfer happens.
        /// @param from The account sending the tokens.
        /// @param to The account receiving the tokens.
        /// @param amount The amount of tokens transferred.
        event Transfer(address indexed from, address indexed to, uint256 amount);
        /// CONSTANT FUNCTIONS ///
        /// @notice Returns the remaining number of tokens that `spender` will be allowed to spend
        /// on behalf of `owner` through {transferFrom}. This is zero by default.
        ///
        /// @dev This value changes when {approve} or {transferFrom} are called.
        function allowance(address owner, address spender) external view returns (uint256);
        /// @notice Returns the amount of tokens owned by `account`.
        function balanceOf(address account) external view returns (uint256);
        /// @notice Returns the number of decimals used to get its user representation.
        function decimals() external view returns (uint8);
        /// @notice Returns the name of the token.
        function name() external view returns (string memory);
        /// @notice Returns the symbol of the token, usually a shorter version of the name.
        function symbol() external view returns (string memory);
        /// @notice Returns the amount of tokens in existence.
        function totalSupply() external view returns (uint256);
        /// NON-CONSTANT FUNCTIONS ///
        /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
        ///
        /// @dev Emits an {Approval} event.
        ///
        /// 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
        ///
        /// Requirements:
        ///
        /// - `spender` cannot be the zero address.
        ///
        /// @return a boolean value indicating whether the operation succeeded.
        function approve(address spender, uint256 amount) external returns (bool);
        /// @notice Atomically decreases the allowance granted to `spender` by the caller.
        ///
        /// @dev Emits an {Approval} event indicating the updated allowance.
        ///
        /// This is an alternative to {approve} that can be used as a mitigation for problems described
        /// in {Erc20Interface-approve}.
        ///
        /// Requirements:
        ///
        /// - `spender` cannot be the zero address.
        /// - `spender` must have allowance for the caller of at least `subtractedValue`.
        function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
        /// @notice Atomically increases the allowance granted to `spender` by the caller.
        ///
        /// @dev Emits an {Approval} event indicating the updated allowance.
        ///
        /// This is an alternative to {approve} that can be used as a mitigation for the problems described above.
        ///
        /// Requirements:
        ///
        /// - `spender` cannot be the zero address.
        function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
        /// @notice Moves `amount` tokens from the caller's account to `recipient`.
        ///
        /// @dev Emits a {Transfer} event.
        ///
        /// Requirements:
        ///
        /// - `recipient` cannot be the zero address.
        /// - The caller must have a balance of at least `amount`.
        ///
        /// @return a boolean value indicating whether the operation succeeded.
        function transfer(address recipient, uint256 amount) external returns (bool);
        /// @notice Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount`
        /// `is then deducted from the caller's allowance.
        ///
        /// @dev Emits a {Transfer} event and an {Approval} event indicating the updated allowance. This is
        /// not required by the Erc. See the note at the beginning of {Erc20}.
        ///
        /// Requirements:
        ///
        /// - `sender` and `recipient` cannot be the zero address.
        /// - `sender` must have a balance of at least `amount`.
        /// - The caller must have approed `sender` to spent at least `amount` tokens.
        ///
        /// @return a boolean value indicating whether the operation succeeded.
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
    }
    

    File 3 of 3: Projects
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
    import "./abstract/Operatable.sol";
    import "./interfaces/IProjects.sol";
    import "./libraries/Operations.sol";
    /** 
      @notice 
      Stores project ownership and identifying information.
      @dev
      Projects are represented as ERC-721's.
    */
    contract Projects is ERC721, IProjects, Operatable {
        // --- private stored properties --- //
        // The number of seconds in a day.
        uint256 private constant SECONDS_IN_YEAR = 31536000;
        // --- public stored properties --- //
        /// @notice A running count of project IDs.
        uint256 public override count = 0;
        /// @notice Optional mapping for project URIs
        mapping(uint256 => string) public override uriOf;
        /// @notice Each project's handle.
        mapping(uint256 => bytes32) public override handleOf;
        /// @notice The project that each unique handle represents.
        mapping(bytes32 => uint256) public override projectFor;
        /// @notice Handles that have been transfered to the specified address.
        mapping(bytes32 => address) public override transferAddressFor;
        /// @notice The timestamps when each handle is claimable. A value of 0 means a handle isn't being challenged.
        mapping(bytes32 => uint256) public override challengeExpiryOf;
        // --- external views --- //
        /** 
          @notice 
          Whether the specified project exists.
          @param _projectId The project to check the existence of.
          @return A flag indicating if the project exists.
        */
        function exists(uint256 _projectId) external view override returns (bool) {
            return _exists(_projectId);
        }
        // --- external transactions --- //
        /** 
          @param _operatorStore A contract storing operator assignments.
        */
        constructor(IOperatorStore _operatorStore)
            ERC721("Juicebox project", "JUICEBOX PROJECT")
            Operatable(_operatorStore)
        {}
        /**
            @notice 
            Create a new project.
            @dev 
            Anyone can create a project on an owner's behalf.
            @param _owner The owner of the project.
            @param _handle A unique handle for the project.
            @param _uri An ipfs CID to more info about the project.
            @param _terminal The terminal to set for this project so that it can start receiving payments.
            @return The new project's ID.
        */
        function create(
            address _owner,
            bytes32 _handle,
            string calldata _uri,
            ITerminal _terminal
        ) external override returns (uint256) {
            // Handle must exist.
            require(_handle != bytes32(0), "Projects::create: EMPTY_HANDLE");
            // Handle must be unique.
            require(
                projectFor[_handle] == 0 &&
                    transferAddressFor[_handle] == address(0),
                "Projects::create: HANDLE_TAKEN"
            );
            // Increment the count, which will be used as the ID.
            count++;
            // Mint the project.
            _safeMint(_owner, count);
            // Set the handle stored values.
            handleOf[count] = _handle;
            projectFor[_handle] = count;
            // Set the URI if one was provided.
            if (bytes(_uri).length > 0) uriOf[count] = _uri;
            // Set the project's terminal if needed.
            if (_terminal != ITerminal(address(0)))
                _terminal.terminalDirectory().setTerminal(count, _terminal);
            emit Create(count, _owner, _handle, _uri, _terminal, msg.sender);
            return count;
        }
        /**
          @notice 
          Allows a project owner to set the project's handle.
          @dev 
          Only a project's owner or operator can set its handle.
          @param _projectId The ID of the project.
          @param _handle The new unique handle for the project.
        */
        function setHandle(uint256 _projectId, bytes32 _handle)
            external
            override
            requirePermission(ownerOf(_projectId), _projectId, Operations.SetHandle)
        {
            // Handle must exist.
            require(_handle != bytes32(0), "Projects::setHandle: EMPTY_HANDLE");
            // Handle must be unique.
            require(
                projectFor[_handle] == 0 &&
                    transferAddressFor[_handle] == address(0),
                "Projects::setHandle: HANDLE_TAKEN"
            );
            // Register the change in the resolver.
            projectFor[handleOf[_projectId]] = 0;
            projectFor[_handle] = _projectId;
            handleOf[_projectId] = _handle;
            emit SetHandle(_projectId, _handle, msg.sender);
        }
        /**
          @notice 
          Allows a project owner to set the project's uri.
          @dev 
          Only a project's owner or operator can set its uri.
          @param _projectId The ID of the project.
          @param _uri An ipfs CDN to more info about the project. Don't include the leading ipfs://
        */
        function setUri(uint256 _projectId, string calldata _uri)
            external
            override
            requirePermission(ownerOf(_projectId), _projectId, Operations.SetUri)
        {
            // Set the new uri.
            uriOf[_projectId] = _uri;
            emit SetUri(_projectId, _uri, msg.sender);
        }
        /**
          @notice 
          Allows a project owner to transfer its handle to another address.
          @dev 
          Only a project's owner or operator can transfer its handle.
          @param _projectId The ID of the project to transfer the handle from.
          @param _to The address that can now reallocate the handle.
          @param _newHandle The new unique handle for the project that will replace the transfered one.
        */
        function transferHandle(
            uint256 _projectId,
            address _to,
            bytes32 _newHandle
        )
            external
            override
            requirePermission(ownerOf(_projectId), _projectId, Operations.SetHandle)
            returns (bytes32 _handle)
        {
            require(
                _newHandle != bytes32(0),
                "Projects::transferHandle: EMPTY_HANDLE"
            );
            require(
                projectFor[_newHandle] == 0 &&
                    transferAddressFor[_handle] == address(0),
                "Projects::transferHandle: HANDLE_TAKEN"
            );
            // Get a reference to the project's currency handle.
            _handle = handleOf[_projectId];
            // Remove the resolver for the transfered handle.
            projectFor[_handle] = 0;
            // If the handle is changing, register the change in the resolver.
            projectFor[_newHandle] = _projectId;
            handleOf[_projectId] = _newHandle;
            // Transfer the current handle.
            transferAddressFor[_handle] = _to;
            emit TransferHandle(_projectId, _to, _handle, _newHandle, msg.sender);
        }
        /**
          @notice 
          Allows an address to claim and handle that has been transfered to them and apply it to a project of theirs.
          @dev 
          Only a project's owner or operator can claim a handle onto it.
          @param _handle The handle being claimed.
          @param _for The address that the handle has been transfered to.
          @param _projectId The ID of the project to use the claimed handle.
        */
        function claimHandle(
            bytes32 _handle,
            address _for,
            uint256 _projectId
        )
            external
            override
            requirePermissionAllowingWildcardDomain(
                _for,
                _projectId,
                Operations.ClaimHandle
            )
            requirePermission(
                ownerOf(_projectId),
                _projectId,
                Operations.ClaimHandle
            )
        {
            // The handle must have been transfered to the specified address,
            // or the handle challange must have expired before being renewed.
            require(
                transferAddressFor[_handle] == _for ||
                    (challengeExpiryOf[_handle] > 0 &&
                        block.timestamp > challengeExpiryOf[_handle]),
                "Projects::claimHandle: UNAUTHORIZED"
            );
            // Register the change in the resolver.
            projectFor[handleOf[_projectId]] = 0;
            // Register the change in the resolver.
            projectFor[_handle] = _projectId;
            // Set the new handle.
            handleOf[_projectId] = _handle;
            // Set the handle as not being transfered.
            transferAddressFor[_handle] = address(0);
            // Reset the challenge to 0.
            challengeExpiryOf[_handle] = 0;
            emit ClaimHandle(_for, _projectId, _handle, msg.sender);
        }
        /** 
          @notice
          Allows anyone to challenge a project's handle. After one year, the handle can be claimed by the public if the challenge isn't answered by the handle's project.
          This can be used to make sure a handle belonging to an unattended to project isn't lost forever.
          @param _handle The handle to challenge.
        */
        function challengeHandle(bytes32 _handle) external {
            // No need to challenge a handle that's not taken.
            require(
                projectFor[_handle] > 0,
                "Projects::challenge: HANDLE_NOT_TAKEN"
            );
            // No need to challenge again if a handle is already being challenged.
            require(
                challengeExpiryOf[_handle] == 0,
                "Projects::challenge: HANDLE_ALREADY_BEING_CHALLENGED"
            );
            // The challenge will expire in a year, at which point the handle can be claimed if the challenge hasn't been answered.
            uint256 _challengeExpiry = block.timestamp + SECONDS_IN_YEAR;
            challengeExpiryOf[_handle] = _challengeExpiry;
            emit ChallengeHandle(_handle, _challengeExpiry, msg.sender);
        }
        /** 
          @notice
          Allows a project to renew its handle so it can't be claimed until a year after its challenged again.
          @dev 
          Only a project's owner or operator can renew its handle.
          @param _projectId The ID of the project that current has the handle being renewed.
        */
        function renewHandle(uint256 _projectId)
            external
            requirePermission(
                ownerOf(_projectId),
                _projectId,
                Operations.RenewHandle
            )
        {
            // Get the handle of the project.
            bytes32 _handle = handleOf[_projectId];
            // Reset the challenge to 0.
            challengeExpiryOf[_handle] = 0;
            emit RenewHandle(_handle, _projectId, msg.sender);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./IERC721.sol";
    import "./IERC721Receiver.sol";
    import "./extensions/IERC721Metadata.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/Strings.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
     * the Metadata extension, but not including the Enumerable extension, which is available separately as
     * {ERC721Enumerable}.
     */
    contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
        using Address for address;
        using Strings for uint256;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Mapping from token ID to owner address
        mapping(uint256 => address) private _owners;
        // Mapping owner address to token count
        mapping(address => uint256) private _balances;
        // Mapping from token ID to approved address
        mapping(uint256 => address) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        /**
         * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC721Metadata).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721-balanceOf}.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            require(owner != address(0), "ERC721: balance query for the zero address");
            return _balances[owner];
        }
        /**
         * @dev See {IERC721-ownerOf}.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            address owner = _owners[tokenId];
            require(owner != address(0), "ERC721: owner query for nonexistent token");
            return owner;
        }
        /**
         * @dev See {IERC721Metadata-name}.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev See {IERC721Metadata-symbol}.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
            string memory baseURI = _baseURI();
            return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, can be overriden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return "";
        }
        /**
         * @dev See {IERC721-approve}.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721.ownerOf(tokenId);
            require(to != owner, "ERC721: approval to current owner");
            require(
                _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                "ERC721: approve caller is not owner nor approved for all"
            );
            _approve(to, tokenId);
        }
        /**
         * @dev See {IERC721-getApproved}.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
            return _tokenApprovals[tokenId];
        }
        /**
         * @dev See {IERC721-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            require(operator != _msgSender(), "ERC721: approve to caller");
            _operatorApprovals[_msgSender()][operator] = approved;
            emit ApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC721-isApprovedForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        /**
         * @dev See {IERC721-transferFrom}.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _transfer(from, to, tokenId);
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) public virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _safeTransfer(from, to, tokenId, _data);
        }
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * `_data` is additional data, it has no specified format and it is sent in call to `to`.
         *
         * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
         * implement alternative mechanisms to perform token transfer, such as signature-based.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeTransfer(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _transfer(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted (`_mint`),
         * and stop existing when they are burned (`_burn`).
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return _owners[tokenId] != address(0);
        }
        /**
         * @dev Returns whether `spender` is allowed to manage `tokenId`.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
            address owner = ERC721.ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
        }
        /**
         * @dev Safely mints `tokenId` and transfers it to `to`.
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
        /**
         * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
         * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
         */
        function _safeMint(
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _mint(to, tokenId);
            require(
                _checkOnERC721Received(address(0), to, tokenId, _data),
                "ERC721: transfer to non ERC721Receiver implementer"
            );
        }
        /**
         * @dev Mints `tokenId` and transfers it to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - `to` cannot be the zero address.
         *
         * Emits a {Transfer} event.
         */
        function _mint(address to, uint256 tokenId) internal virtual {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!_exists(tokenId), "ERC721: token already minted");
            _beforeTokenTransfer(address(0), to, tokenId);
            _balances[to] += 1;
            _owners[tokenId] = to;
            emit Transfer(address(0), to, tokenId);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721.ownerOf(tokenId);
            _beforeTokenTransfer(owner, address(0), tokenId);
            // Clear approvals
            _approve(address(0), tokenId);
            _balances[owner] -= 1;
            delete _owners[tokenId];
            emit Transfer(owner, address(0), tokenId);
        }
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         *
         * Emits a {Transfer} event.
         */
        function _transfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {
            require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
            require(to != address(0), "ERC721: transfer to the zero address");
            _beforeTokenTransfer(from, to, tokenId);
            // Clear approvals from the previous owner
            _approve(address(0), tokenId);
            _balances[from] -= 1;
            _balances[to] += 1;
            _owners[tokenId] = to;
            emit Transfer(from, to, tokenId);
        }
        /**
         * @dev Approve `to` to operate on `tokenId`
         *
         * Emits a {Approval} event.
         */
        function _approve(address to, uint256 tokenId) internal virtual {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            if (to.isContract()) {
                try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                    return retval == IERC721Receiver(to).onERC721Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert("ERC721: transfer to non ERC721Receiver implementer");
                    } else {
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, ``from``'s `tokenId` will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./../interfaces/IOperatable.sol";
    abstract contract Operatable is IOperatable {
        modifier requirePermission(
            address _account,
            uint256 _domain,
            uint256 _index
        ) {
            require(
                msg.sender == _account ||
                    operatorStore.hasPermission(
                        msg.sender,
                        _account,
                        _domain,
                        _index
                    ),
                "Operatable: UNAUTHORIZED"
            );
            _;
        }
        modifier requirePermissionAllowingWildcardDomain(
            address _account,
            uint256 _domain,
            uint256 _index
        ) {
            require(
                msg.sender == _account ||
                    operatorStore.hasPermission(
                        msg.sender,
                        _account,
                        _domain,
                        _index
                    ) ||
                    operatorStore.hasPermission(msg.sender, _account, 0, _index),
                "Operatable: UNAUTHORIZED"
            );
            _;
        }
        modifier requirePermissionAcceptingAlternateAddress(
            address _account,
            uint256 _domain,
            uint256 _index,
            address _alternate
        ) {
            require(
                msg.sender == _account ||
                    operatorStore.hasPermission(
                        msg.sender,
                        _account,
                        _domain,
                        _index
                    ) ||
                    msg.sender == _alternate,
                "Operatable: UNAUTHORIZED"
            );
            _;
        }
        /// @notice A contract storing operator assignments.
        IOperatorStore public immutable override operatorStore;
        /** 
          @param _operatorStore A contract storing operator assignments.
        */
        constructor(IOperatorStore _operatorStore) {
            operatorStore = _operatorStore;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "./ITerminal.sol";
    import "./IOperatorStore.sol";
    interface IProjects is IERC721 {
        event Create(
            uint256 indexed projectId,
            address indexed owner,
            bytes32 indexed handle,
            string uri,
            ITerminal terminal,
            address caller
        );
        event SetHandle(
            uint256 indexed projectId,
            bytes32 indexed handle,
            address caller
        );
        event SetUri(uint256 indexed projectId, string uri, address caller);
        event TransferHandle(
            uint256 indexed projectId,
            address indexed to,
            bytes32 indexed handle,
            bytes32 newHandle,
            address caller
        );
        event ClaimHandle(
            address indexed account,
            uint256 indexed projectId,
            bytes32 indexed handle,
            address caller
        );
        event ChallengeHandle(
            bytes32 indexed handle,
            uint256 challengeExpiry,
            address caller
        );
        event RenewHandle(
            bytes32 indexed handle,
            uint256 indexed projectId,
            address caller
        );
        function count() external view returns (uint256);
        function uriOf(uint256 _projectId) external view returns (string memory);
        function handleOf(uint256 _projectId) external returns (bytes32 handle);
        function projectFor(bytes32 _handle) external returns (uint256 projectId);
        function transferAddressFor(bytes32 _handle)
            external
            returns (address receiver);
        function challengeExpiryOf(bytes32 _handle) external returns (uint256);
        function exists(uint256 _projectId) external view returns (bool);
        function create(
            address _owner,
            bytes32 _handle,
            string calldata _uri,
            ITerminal _terminal
        ) external returns (uint256 id);
        function setHandle(uint256 _projectId, bytes32 _handle) external;
        function setUri(uint256 _projectId, string calldata _uri) external;
        function transferHandle(
            uint256 _projectId,
            address _to,
            bytes32 _newHandle
        ) external returns (bytes32 _handle);
        function claimHandle(
            bytes32 _handle,
            address _for,
            uint256 _projectId
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    library Operations {
        uint256 public constant Configure = 1;
        uint256 public constant PrintPreminedTickets = 2;
        uint256 public constant Redeem = 3;
        uint256 public constant Migrate = 4;
        uint256 public constant SetHandle = 5;
        uint256 public constant SetUri = 6;
        uint256 public constant ClaimHandle = 7;
        uint256 public constant RenewHandle = 8;
        uint256 public constant Issue = 9;
        uint256 public constant Stake = 10;
        uint256 public constant Unstake = 11;
        uint256 public constant Transfer = 12;
        uint256 public constant Lock = 13;
        uint256 public constant SetPayoutMods = 14;
        uint256 public constant SetTicketMods = 15;
        uint256 public constant SetTerminal = 16;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    interface IERC721Receiver {
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
         */
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Metadata is IERC721 {
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            assembly {
                size := extcodesize(account)
            }
            return size > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            (bool success, bytes memory returndata) = target.staticcall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        function _verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) private pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    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.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./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);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    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);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./IOperatorStore.sol";
    interface IOperatable {
        function operatorStore() external view returns (IOperatorStore);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    interface IOperatorStore {
        event SetOperator(
            address indexed operator,
            address indexed account,
            uint256 indexed domain,
            uint256[] permissionIndexes,
            uint256 packed
        );
        function permissionsOf(
            address _operator,
            address _account,
            uint256 _domain
        ) external view returns (uint256);
        function hasPermission(
            address _operator,
            address _account,
            uint256 _domain,
            uint256 _permissionIndex
        ) external view returns (bool);
        function hasPermissions(
            address _operator,
            address _account,
            uint256 _domain,
            uint256[] calldata _permissionIndexes
        ) external view returns (bool);
        function setOperator(
            address _operator,
            uint256 _domain,
            uint256[] calldata _permissionIndexes
        ) external;
        function setOperators(
            address[] calldata _operators,
            uint256[] calldata _domains,
            uint256[][] calldata _permissionIndexes
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./ITerminalDirectory.sol";
    interface ITerminal {
        event Pay(
            uint256 indexed fundingCycleId,
            uint256 indexed projectId,
            address indexed beneficiary,
            uint256 amount,
            string note,
            address caller
        );
        event AddToBalance(
            uint256 indexed projectId,
            uint256 value,
            address caller
        );
        event AllowMigration(ITerminal allowed);
        event Migrate(
            uint256 indexed projectId,
            ITerminal indexed to,
            uint256 _amount,
            address caller
        );
        function terminalDirectory() external view returns (ITerminalDirectory);
        function migrationIsAllowed(ITerminal _terminal)
            external
            view
            returns (bool);
        function pay(
            uint256 _projectId,
            address _beneficiary,
            string calldata _memo,
            bool _preferUnstakedTickets
        ) external payable returns (uint256 fundingCycleId);
        function addToBalance(uint256 _projectId) external payable;
        function allowMigration(ITerminal _contract) external;
        function migrate(uint256 _projectId, ITerminal _to) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./IDirectPaymentAddress.sol";
    import "./ITerminal.sol";
    import "./IProjects.sol";
    import "./IProjects.sol";
    interface ITerminalDirectory {
        event DeployAddress(
            uint256 indexed projectId,
            string memo,
            address indexed caller
        );
        event SetTerminal(
            uint256 indexed projectId,
            ITerminal indexed terminal,
            address caller
        );
        event SetPayerPreferences(
            address indexed account,
            address beneficiary,
            bool preferUnstakedTickets
        );
        function projects() external view returns (IProjects);
        function terminalOf(uint256 _projectId) external view returns (ITerminal);
        function beneficiaryOf(address _account) external returns (address);
        function unstakedTicketsPreferenceOf(address _account)
            external
            returns (bool);
        function addressesOf(uint256 _projectId)
            external
            view
            returns (IDirectPaymentAddress[] memory);
        function deployAddress(uint256 _projectId, string calldata _memo) external;
        function setTerminal(uint256 _projectId, ITerminal _terminal) external;
        function setPayerPreferences(
            address _beneficiary,
            bool _preferUnstakedTickets
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.6;
    import "./ITerminalDirectory.sol";
    import "./ITerminal.sol";
    interface IDirectPaymentAddress {
        event Forward(
            address indexed payer,
            uint256 indexed projectId,
            address beneficiary,
            uint256 value,
            string memo,
            bool preferUnstakedTickets
        );
        function terminalDirectory() external returns (ITerminalDirectory);
        function projectId() external returns (uint256);
        function memo() external returns (string memory);
    }