ETH Price: $2,350.02 (+7.39%)

Transaction Decoder

Block:
18397568 at Oct-21-2023 08:43:11 AM +UTC
Transaction Fee:
0.009254932017428922 ETH $21.75
Gas Used:
768,662 Gas / 12.040314231 Gwei

Emitted Events:

178 DiamondProxy.0x22c9005dd88c18b552a1cd7e8b3b937fcde9ca69213c1f658f54d572e4877a81( 0x22c9005dd88c18b552a1cd7e8b3b937fcde9ca69213c1f658f54d572e4877a81, 0x0000000000000000000000000000000000000000000000000000000000044202, 0x0000000000000000000000000000000000000000000000000000000000044203 )

Account State Difference:

  Address   Before After State Difference Code
0x32400084...60a000324
(zkSync Era: Diamond Proxy)
0x35274399...f6e572092
(zkSync Era: Validator)
62.827315380222759467 Eth
Nonce: 409876
62.818060448205330545 Eth
Nonce: 409877
0.009254932017428922
(Titan Builder)
16.571063152475549202 Eth16.573850503489356598 Eth0.002787351013807396

Execution Trace

ValidatorTimelock.proveBlocks( [{name:blockNumber, type:uint64, order:1, indexed:false, value:279042, valueString:279042}, {name:blockHash, type:bytes32, order:2, indexed:false, value:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6, valueString:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:155112084, valueString:155112084}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470, valueString:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2, valueString:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2}, {name:timestamp, type:uint256, order:7, indexed:false, value:1697864134, valueString:1697864134}, {name:commitment, type:bytes32, order:8, indexed:false, value:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D, valueString:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D}], , [{name:recursiveAggregationInput, type:uint256[], order:1, indexed:false, value:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833], valueString:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833]}, {name:serializedProof, type:uint256[], order:2, indexed:false, value:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477], valueString:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477]}] )
  • DiamondProxy.7739cbe7( )
    • ExecutorFacet.proveBlocks( _prevBlock=[{name:blockNumber, type:uint64, order:1, indexed:false, value:279042, valueString:279042}, {name:blockHash, type:bytes32, order:2, indexed:false, value:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6, valueString:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:155112084, valueString:155112084}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470, valueString:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2, valueString:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2}, {name:timestamp, type:uint256, order:7, indexed:false, value:1697864134, valueString:1697864134}, {name:commitment, type:bytes32, order:8, indexed:false, value:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D, valueString:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D}], _committedBlocks=, _proof=[{name:recursiveAggregationInput, type:uint256[], order:1, indexed:false, value:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833], valueString:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833]}, {name:serializedProof, type:uint256[], order:2, indexed:false, value:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477], valueString:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477]}] )
      • Verifier.verify_serialized_proof( public_inputs=[381921557989928181181384233298289158277820569422286091151545044311181834105], serialized_proof=[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477] ) => ( True )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...007.20b0b224( )
        • Null: 0x000...006.1794ea12( )
        • Null: 0x000...007.2a50c8a8( )
        • Null: 0x000...006.0d6089f6( )
        • Null: 0x000...007.241729bd( )
        • Null: 0x000...006.15fc6ddd( )
        • Null: 0x000...007.1b7d54f8( )
        • Null: 0x000...006.0ebe0de4( )
        • Null: 0x000...007.0138733c( )
        • Null: 0x000...006.0c8f6a9f( )
        • Null: 0x000...007.1b1be727( )
        • Null: 0x000...006.122d63da( )
        • Null: 0x000...007.14c289d7( )
        • Null: 0x000...007.210b5cc8( )
        • Null: 0x000...006.2174774b( )
        • Null: 0x000...007.29d4d14a( )
        • Null: 0x000...006.04c46cdf( )
        • Null: 0x000...007.06d15382( )
        • Null: 0x000...006.2ffcbb86( )
        • Null: 0x000...007.274a668d( )
        • Null: 0x000...006.152af99b( )
        • Null: 0x000...007.0ba53bf4( )
        • Null: 0x000...006.14bc1172( )
        • Null: 0x000...006.0dbd4fb5( )
        • Null: 0x000...007.09259595( )
        • Null: 0x000...006.1ce29553( )
        • Null: 0x000...007.19028eb9( )
        • Null: 0x000...007.143471a1( )
        • Null: 0x000...006.2622b31c( )
        • Null: 0x000...007.12ab49cd( )
        • Null: 0x000...006.00fde849( )
        • Null: 0x000...007.2f3ede68( )
        • Null: 0x000...006.0596bd67( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...005.00000000( )
        • Null: 0x000...007.12ab49cd( )
        • Null: 0x000...006.1381e59a( )
        • Null: 0x000...007.0fd89d40( )
        • Null: 0x000...007.1b74cd19( )
        • Null: 0x000...006.1f4a6877( )
        • Null: 0x000...006.0df5a253( )
        • Null: 0x000...007.2512bbc0( )
        • Null: 0x000...006.20177830( )
        • Null: 0x000...007.07ca93d0( )
        • Null: 0x000...006.2427610f( )
        • Null: 0x000...007.2042d774( )
        • Null: 0x000...006.288384b4( )
        • Null: 0x000...007.10295239( )
        • Null: 0x000...006.1337e898( )
        • Null: 0x000...007.273b141a( )
        • Null: 0x000...006.010feec4( )
        • Null: 0x000...007.28f2a0a9( )
        • Null: 0x000...006.2f87dede( )
        • Null: 0x000...007.1423fa82( )
        • Null: 0x000...006.17be9526( )
        • Null: 0x000...007.0e9b5b12( )
        • Null: 0x000...006.2fd213d8( )
        • Null: 0x000...007.25d1a714( )
        • Null: 0x000...006.2a6e1648( )
        • Null: 0x000...007.201a04b4( )
        • Null: 0x000...006.0f89d993( )
        • Null: 0x000...007.1f814e2d( )
        • Null: 0x000...006.1f3659e9( )
        • Null: 0x000...007.2f85df2d( )
        • Null: 0x000...006.05e9e800( )
        • Null: 0x000...007.12ab49cd( )
        • Null: 0x000...007.273b141a( )
        • Null: 0x000...006.2d85f214( )
        • Null: 0x000...007.0fd89d40( )
        • Null: 0x000...006.14d4d84f( )
        • Null: 0x000...007.1b74cd19( )
        • Null: 0x000...006.14e0bb3f( )
        • Null: 0x000...007.201a04b4( )
        • Null: 0x000...006.13d6f949( )
        • Null: 0x000...007.00000000( )
        • Null: 0x000...007.1efc9e34( )
        • Null: 0x000...006.08fe87db( )
        • Null: 0x000...007.00000000( )
        • Null: 0x000...006.1e4d84a1( )
        • Null: 0x000...007.0db7d705( )
        • Null: 0x000...007.2d7e36b4( )
        • Null: 0x000...006.2dbb4032( )
        • Null: 0x000...006.1d11ff0a( )
        • Null: 0x000...007.2d7e36b4( )
        • Null: 0x000...006.2e62903e( )
        • Null: 0x000...008.1050904d( )
        • Null: 0x000...008.1b973618( )
          File 1 of 4: ValidatorTimelock
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)
          pragma solidity ^0.8.0;
          import "./Ownable.sol";
          /**
           * @dev Contract module which provides 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} and {acceptOwnership}.
           *
           * This module is used through inheritance. It will make available all functions
           * from parent (Ownable).
           */
          abstract contract Ownable2Step is Ownable {
              address private _pendingOwner;
              event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Returns the address of the pending owner.
               */
              function pendingOwner() public view virtual returns (address) {
                  return _pendingOwner;
              }
              /**
               * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual override onlyOwner {
                  _pendingOwner = newOwner;
                  emit OwnershipTransferStarted(owner(), newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual override {
                  delete _pendingOwner;
                  super._transferOwnership(newOwner);
              }
              /**
               * @dev The new owner accepts the ownership transfer.
               */
              function acceptOwnership() external {
                  address sender = _msgSender();
                  require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
                  _transferOwnership(sender);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          interface IExecutor {
              /// @notice Rollup block stored data
              /// @param blockNumber Rollup block number
              /// @param blockHash Hash of L2 block
              /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
              /// @param numberOfLayer1Txs Number of priority operations to be processed
              /// @param priorityOperationsHash Hash of all priority operations from this block
              /// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block
              /// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant
              /// @param commitment Verified input for the zkSync circuit
              struct StoredBlockInfo {
                  uint64 blockNumber;
                  bytes32 blockHash;
                  uint64 indexRepeatedStorageChanges;
                  uint256 numberOfLayer1Txs;
                  bytes32 priorityOperationsHash;
                  bytes32 l2LogsTreeRoot;
                  uint256 timestamp;
                  bytes32 commitment;
              }
              /// @notice Data needed to commit new block
              /// @param blockNumber Number of the committed block
              /// @param timestamp Unix timestamp denoting the start of the block execution
              /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
              /// @param newStateRoot The state root of the full state tree
              /// @param numberOfLayer1Txs Number of priority operations to be processed
              /// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block
              /// @param priorityOperationsHash Hash of all priority operations from this block
              /// @param initialStorageChanges Storage write access as a concatenation key-value
              /// @param repeatedStorageChanges Storage write access as a concatenation index-value
              /// @param l2Logs concatenation of all L2 -> L1 logs in the block
              /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract
              /// @param factoryDeps array of l2 bytecodes that were marked as known on L2
              struct CommitBlockInfo {
                  uint64 blockNumber;
                  uint64 timestamp;
                  uint64 indexRepeatedStorageChanges;
                  bytes32 newStateRoot;
                  uint256 numberOfLayer1Txs;
                  bytes32 l2LogsTreeRoot;
                  bytes32 priorityOperationsHash;
                  bytes initialStorageChanges;
                  bytes repeatedStorageChanges;
                  bytes l2Logs;
                  bytes[] l2ArbitraryLengthMessages;
                  bytes[] factoryDeps;
              }
              /// @notice Recursive proof input data (individual commitments are constructed onchain)
              struct ProofInput {
                  uint256[] recursiveAggregationInput;
                  uint256[] serializedProof;
              }
              function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
                  external;
              function proveBlocks(
                  StoredBlockInfo calldata _prevBlock,
                  StoredBlockInfo[] calldata _committedBlocks,
                  ProofInput calldata _proof
              ) external;
              function executeBlocks(StoredBlockInfo[] calldata _blocksData) external;
              function revertBlocks(uint256 _newLastBlock) external;
              /// @notice Event emitted when a block is committed
              event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
              /// @notice Event emitted when blocks are verified
              event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock);
              /// @notice Event emitted when a block is executed
              event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
              /// @notice Event emitted when blocks are reverted
              event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "@openzeppelin/contracts/access/Ownable2Step.sol";
          import "./interfaces/IExecutor.sol";
          /// @author Matter Labs
          /// @notice Intermediate smart contract between the validator EOA account and the zkSync smart contract.
          /// @dev The primary purpose of this contract is to provide a trustless means of delaying block execution without
          /// modifying the main zkSync contract. As such, even if this contract is compromised, it will not impact the main contract.
          /// @dev zkSync actively monitors the chain activity and reacts to any suspicious activity by freezing the chain.
          /// This allows time for investigation and mitigation before resuming normal operations.
          /// @dev The contract overloads all of the 4 methods, that are used in state transition. When the block is committed, the
          /// timestamp is stored for it. Later, when the owner calls the block execution, the contract checks that block
          /// was committed not earlier than X time ago.
          contract ValidatorTimelock is IExecutor, Ownable2Step {
              /// @notice The delay between committing and executing blocks is changed.
              event NewExecutionDelay(uint256 _newExecutionDelay);
              /// @notice The validator address is changed.
              event NewValidator(address _oldValidator, address _newValidator);
              /// @dev The main zkSync smart contract.
              address public immutable zkSyncContract;
              /// @dev The mapping of L2 block number => timestamp when it was commited.
              mapping(uint256 => uint256) public committedBlockTimestamp;
              /// @dev The address that can commit/revert/validate/execute blocks.
              address public validator;
              /// @dev The delay between committing and executing blocks.
              uint256 public executionDelay;
              constructor(
                  address _initialOwner,
                  address _zkSyncContract,
                  uint256 _executionDelay,
                  address _validator
              ) {
                  _transferOwnership(_initialOwner);
                  zkSyncContract = _zkSyncContract;
                  executionDelay = _executionDelay;
                  validator = _validator;
              }
              /// @dev Set new validator address.
              function setValidator(address _newValidator) external onlyOwner {
                  address oldValidator = validator;
                  validator = _newValidator;
                  emit NewValidator(oldValidator, _newValidator);
              }
              /// @dev Set the delay between committing and executing blocks.
              function setExecutionDelay(uint256 _executionDelay) external onlyOwner {
                  executionDelay = _executionDelay;
                  emit NewExecutionDelay(_executionDelay);
              }
              /// @notice Checks if the caller is a validator.
              modifier onlyValidator() {
                  require(msg.sender == validator, "8h");
                  _;
              }
              /// @dev Records the timestamp for all provided committed blocks and make
              /// a call to the zkSync contract with the same calldata.
              function commitBlocks(StoredBlockInfo calldata, CommitBlockInfo[] calldata _newBlocksData) external onlyValidator {
                  for (uint256 i = 0; i < _newBlocksData.length; ++i) {
                      committedBlockTimestamp[_newBlocksData[i].blockNumber] = block.timestamp;
                  }
                  _propagateToZkSync();
              }
              /// @dev Make a call to the zkSync contract with the same calldata.
              /// Note: If the block is reverted, it needs to be committed first before the execution.
              /// So it's safe to not override the committed blocks.
              function revertBlocks(uint256) external onlyValidator {
                  _propagateToZkSync();
              }
              /// @dev Make a call to the zkSync contract with the same calldata.
              /// Note: We don't track the time when blocks are proven, since all information about
              /// the block is known on the commit stage and the proved is not finalized (may be reverted).
              function proveBlocks(
                  StoredBlockInfo calldata,
                  StoredBlockInfo[] calldata,
                  ProofInput calldata
              ) external onlyValidator {
                  _propagateToZkSync();
              }
              /// @dev Check that blocks were committed at least X time ago and
              /// make a call to the zkSync contract with the same calldata.
              function executeBlocks(StoredBlockInfo[] calldata _newBlocksData) external onlyValidator {
                  for (uint256 i = 0; i < _newBlocksData.length; ++i) {
                      uint256 commitBlockTimestamp = committedBlockTimestamp[_newBlocksData[i].blockNumber];
                      // Note: if the `commitBlockTimestamp` is zero, that means either:
                      // * The block was committed, but not though this contract.
                      // * The block wasn't committed at all, so execution will fail in the zkSync contract.
                      // We allow executing such blocks.
                      require(block.timestamp > commitBlockTimestamp + executionDelay, "5c"); // The delay is not passed
                  }
                  _propagateToZkSync();
              }
              /// @dev Call the zkSync contract with the same calldata as this contract was called.
              /// Note: it is called the zkSync contract, not delegatecalled!
              function _propagateToZkSync() internal {
                  address contractAddress = zkSyncContract;
                  assembly {
                      // Copy function signature and arguments from calldata at zero position into memory at pointer position
                      calldatacopy(0, 0, calldatasize())
                      // Call method of the zkSync contract returns 0 on error
                      let result := call(gas(), contractAddress, 0, 0, calldatasize(), 0, 0)
                      // Get the size of the last return data
                      let size := returndatasize()
                      // Copy the size length of bytes from return data at zero position to pointer position
                      returndatacopy(0, 0, size)
                      // Depending on the result value
                      switch result
                      case 0 {
                          // End execution and revert state changes
                          revert(0, size)
                      }
                      default {
                          // Return data with length of size at pointers position
                          return(0, size)
                      }
                  }
              }
          }
          

          File 2 of 4: DiamondProxy
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
          // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
          pragma solidity ^0.8.0;
          /**
           * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
           * checks.
           *
           * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
           * easily result in undesired exploitation or bugs, since developers usually
           * assume that overflows raise errors. `SafeCast` restores this intuition by
           * reverting the transaction when such an operation overflows.
           *
           * Using this library instead of the unchecked operations eliminates an entire
           * class of bugs, so it's recommended to use it always.
           *
           * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
           * all math on `uint256` and `int256` and then downcasting.
           */
          library SafeCast {
              /**
               * @dev Returns the downcasted uint248 from uint256, reverting on
               * overflow (when the input is greater than largest uint248).
               *
               * Counterpart to Solidity's `uint248` operator.
               *
               * Requirements:
               *
               * - input must fit into 248 bits
               *
               * _Available since v4.7._
               */
              function toUint248(uint256 value) internal pure returns (uint248) {
                  require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                  return uint248(value);
              }
              /**
               * @dev Returns the downcasted uint240 from uint256, reverting on
               * overflow (when the input is greater than largest uint240).
               *
               * Counterpart to Solidity's `uint240` operator.
               *
               * Requirements:
               *
               * - input must fit into 240 bits
               *
               * _Available since v4.7._
               */
              function toUint240(uint256 value) internal pure returns (uint240) {
                  require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                  return uint240(value);
              }
              /**
               * @dev Returns the downcasted uint232 from uint256, reverting on
               * overflow (when the input is greater than largest uint232).
               *
               * Counterpart to Solidity's `uint232` operator.
               *
               * Requirements:
               *
               * - input must fit into 232 bits
               *
               * _Available since v4.7._
               */
              function toUint232(uint256 value) internal pure returns (uint232) {
                  require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                  return uint232(value);
              }
              /**
               * @dev Returns the downcasted uint224 from uint256, reverting on
               * overflow (when the input is greater than largest uint224).
               *
               * Counterpart to Solidity's `uint224` operator.
               *
               * Requirements:
               *
               * - input must fit into 224 bits
               *
               * _Available since v4.2._
               */
              function toUint224(uint256 value) internal pure returns (uint224) {
                  require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                  return uint224(value);
              }
              /**
               * @dev Returns the downcasted uint216 from uint256, reverting on
               * overflow (when the input is greater than largest uint216).
               *
               * Counterpart to Solidity's `uint216` operator.
               *
               * Requirements:
               *
               * - input must fit into 216 bits
               *
               * _Available since v4.7._
               */
              function toUint216(uint256 value) internal pure returns (uint216) {
                  require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                  return uint216(value);
              }
              /**
               * @dev Returns the downcasted uint208 from uint256, reverting on
               * overflow (when the input is greater than largest uint208).
               *
               * Counterpart to Solidity's `uint208` operator.
               *
               * Requirements:
               *
               * - input must fit into 208 bits
               *
               * _Available since v4.7._
               */
              function toUint208(uint256 value) internal pure returns (uint208) {
                  require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                  return uint208(value);
              }
              /**
               * @dev Returns the downcasted uint200 from uint256, reverting on
               * overflow (when the input is greater than largest uint200).
               *
               * Counterpart to Solidity's `uint200` operator.
               *
               * Requirements:
               *
               * - input must fit into 200 bits
               *
               * _Available since v4.7._
               */
              function toUint200(uint256 value) internal pure returns (uint200) {
                  require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                  return uint200(value);
              }
              /**
               * @dev Returns the downcasted uint192 from uint256, reverting on
               * overflow (when the input is greater than largest uint192).
               *
               * Counterpart to Solidity's `uint192` operator.
               *
               * Requirements:
               *
               * - input must fit into 192 bits
               *
               * _Available since v4.7._
               */
              function toUint192(uint256 value) internal pure returns (uint192) {
                  require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                  return uint192(value);
              }
              /**
               * @dev Returns the downcasted uint184 from uint256, reverting on
               * overflow (when the input is greater than largest uint184).
               *
               * Counterpart to Solidity's `uint184` operator.
               *
               * Requirements:
               *
               * - input must fit into 184 bits
               *
               * _Available since v4.7._
               */
              function toUint184(uint256 value) internal pure returns (uint184) {
                  require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                  return uint184(value);
              }
              /**
               * @dev Returns the downcasted uint176 from uint256, reverting on
               * overflow (when the input is greater than largest uint176).
               *
               * Counterpart to Solidity's `uint176` operator.
               *
               * Requirements:
               *
               * - input must fit into 176 bits
               *
               * _Available since v4.7._
               */
              function toUint176(uint256 value) internal pure returns (uint176) {
                  require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                  return uint176(value);
              }
              /**
               * @dev Returns the downcasted uint168 from uint256, reverting on
               * overflow (when the input is greater than largest uint168).
               *
               * Counterpart to Solidity's `uint168` operator.
               *
               * Requirements:
               *
               * - input must fit into 168 bits
               *
               * _Available since v4.7._
               */
              function toUint168(uint256 value) internal pure returns (uint168) {
                  require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                  return uint168(value);
              }
              /**
               * @dev Returns the downcasted uint160 from uint256, reverting on
               * overflow (when the input is greater than largest uint160).
               *
               * Counterpart to Solidity's `uint160` operator.
               *
               * Requirements:
               *
               * - input must fit into 160 bits
               *
               * _Available since v4.7._
               */
              function toUint160(uint256 value) internal pure returns (uint160) {
                  require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                  return uint160(value);
              }
              /**
               * @dev Returns the downcasted uint152 from uint256, reverting on
               * overflow (when the input is greater than largest uint152).
               *
               * Counterpart to Solidity's `uint152` operator.
               *
               * Requirements:
               *
               * - input must fit into 152 bits
               *
               * _Available since v4.7._
               */
              function toUint152(uint256 value) internal pure returns (uint152) {
                  require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                  return uint152(value);
              }
              /**
               * @dev Returns the downcasted uint144 from uint256, reverting on
               * overflow (when the input is greater than largest uint144).
               *
               * Counterpart to Solidity's `uint144` operator.
               *
               * Requirements:
               *
               * - input must fit into 144 bits
               *
               * _Available since v4.7._
               */
              function toUint144(uint256 value) internal pure returns (uint144) {
                  require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                  return uint144(value);
              }
              /**
               * @dev Returns the downcasted uint136 from uint256, reverting on
               * overflow (when the input is greater than largest uint136).
               *
               * Counterpart to Solidity's `uint136` operator.
               *
               * Requirements:
               *
               * - input must fit into 136 bits
               *
               * _Available since v4.7._
               */
              function toUint136(uint256 value) internal pure returns (uint136) {
                  require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                  return uint136(value);
              }
              /**
               * @dev Returns the downcasted uint128 from uint256, reverting on
               * overflow (when the input is greater than largest uint128).
               *
               * Counterpart to Solidity's `uint128` operator.
               *
               * Requirements:
               *
               * - input must fit into 128 bits
               *
               * _Available since v2.5._
               */
              function toUint128(uint256 value) internal pure returns (uint128) {
                  require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                  return uint128(value);
              }
              /**
               * @dev Returns the downcasted uint120 from uint256, reverting on
               * overflow (when the input is greater than largest uint120).
               *
               * Counterpart to Solidity's `uint120` operator.
               *
               * Requirements:
               *
               * - input must fit into 120 bits
               *
               * _Available since v4.7._
               */
              function toUint120(uint256 value) internal pure returns (uint120) {
                  require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                  return uint120(value);
              }
              /**
               * @dev Returns the downcasted uint112 from uint256, reverting on
               * overflow (when the input is greater than largest uint112).
               *
               * Counterpart to Solidity's `uint112` operator.
               *
               * Requirements:
               *
               * - input must fit into 112 bits
               *
               * _Available since v4.7._
               */
              function toUint112(uint256 value) internal pure returns (uint112) {
                  require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                  return uint112(value);
              }
              /**
               * @dev Returns the downcasted uint104 from uint256, reverting on
               * overflow (when the input is greater than largest uint104).
               *
               * Counterpart to Solidity's `uint104` operator.
               *
               * Requirements:
               *
               * - input must fit into 104 bits
               *
               * _Available since v4.7._
               */
              function toUint104(uint256 value) internal pure returns (uint104) {
                  require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                  return uint104(value);
              }
              /**
               * @dev Returns the downcasted uint96 from uint256, reverting on
               * overflow (when the input is greater than largest uint96).
               *
               * Counterpart to Solidity's `uint96` operator.
               *
               * Requirements:
               *
               * - input must fit into 96 bits
               *
               * _Available since v4.2._
               */
              function toUint96(uint256 value) internal pure returns (uint96) {
                  require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                  return uint96(value);
              }
              /**
               * @dev Returns the downcasted uint88 from uint256, reverting on
               * overflow (when the input is greater than largest uint88).
               *
               * Counterpart to Solidity's `uint88` operator.
               *
               * Requirements:
               *
               * - input must fit into 88 bits
               *
               * _Available since v4.7._
               */
              function toUint88(uint256 value) internal pure returns (uint88) {
                  require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                  return uint88(value);
              }
              /**
               * @dev Returns the downcasted uint80 from uint256, reverting on
               * overflow (when the input is greater than largest uint80).
               *
               * Counterpart to Solidity's `uint80` operator.
               *
               * Requirements:
               *
               * - input must fit into 80 bits
               *
               * _Available since v4.7._
               */
              function toUint80(uint256 value) internal pure returns (uint80) {
                  require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                  return uint80(value);
              }
              /**
               * @dev Returns the downcasted uint72 from uint256, reverting on
               * overflow (when the input is greater than largest uint72).
               *
               * Counterpart to Solidity's `uint72` operator.
               *
               * Requirements:
               *
               * - input must fit into 72 bits
               *
               * _Available since v4.7._
               */
              function toUint72(uint256 value) internal pure returns (uint72) {
                  require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                  return uint72(value);
              }
              /**
               * @dev Returns the downcasted uint64 from uint256, reverting on
               * overflow (when the input is greater than largest uint64).
               *
               * Counterpart to Solidity's `uint64` operator.
               *
               * Requirements:
               *
               * - input must fit into 64 bits
               *
               * _Available since v2.5._
               */
              function toUint64(uint256 value) internal pure returns (uint64) {
                  require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                  return uint64(value);
              }
              /**
               * @dev Returns the downcasted uint56 from uint256, reverting on
               * overflow (when the input is greater than largest uint56).
               *
               * Counterpart to Solidity's `uint56` operator.
               *
               * Requirements:
               *
               * - input must fit into 56 bits
               *
               * _Available since v4.7._
               */
              function toUint56(uint256 value) internal pure returns (uint56) {
                  require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                  return uint56(value);
              }
              /**
               * @dev Returns the downcasted uint48 from uint256, reverting on
               * overflow (when the input is greater than largest uint48).
               *
               * Counterpart to Solidity's `uint48` operator.
               *
               * Requirements:
               *
               * - input must fit into 48 bits
               *
               * _Available since v4.7._
               */
              function toUint48(uint256 value) internal pure returns (uint48) {
                  require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                  return uint48(value);
              }
              /**
               * @dev Returns the downcasted uint40 from uint256, reverting on
               * overflow (when the input is greater than largest uint40).
               *
               * Counterpart to Solidity's `uint40` operator.
               *
               * Requirements:
               *
               * - input must fit into 40 bits
               *
               * _Available since v4.7._
               */
              function toUint40(uint256 value) internal pure returns (uint40) {
                  require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                  return uint40(value);
              }
              /**
               * @dev Returns the downcasted uint32 from uint256, reverting on
               * overflow (when the input is greater than largest uint32).
               *
               * Counterpart to Solidity's `uint32` operator.
               *
               * Requirements:
               *
               * - input must fit into 32 bits
               *
               * _Available since v2.5._
               */
              function toUint32(uint256 value) internal pure returns (uint32) {
                  require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                  return uint32(value);
              }
              /**
               * @dev Returns the downcasted uint24 from uint256, reverting on
               * overflow (when the input is greater than largest uint24).
               *
               * Counterpart to Solidity's `uint24` operator.
               *
               * Requirements:
               *
               * - input must fit into 24 bits
               *
               * _Available since v4.7._
               */
              function toUint24(uint256 value) internal pure returns (uint24) {
                  require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                  return uint24(value);
              }
              /**
               * @dev Returns the downcasted uint16 from uint256, reverting on
               * overflow (when the input is greater than largest uint16).
               *
               * Counterpart to Solidity's `uint16` operator.
               *
               * Requirements:
               *
               * - input must fit into 16 bits
               *
               * _Available since v2.5._
               */
              function toUint16(uint256 value) internal pure returns (uint16) {
                  require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                  return uint16(value);
              }
              /**
               * @dev Returns the downcasted uint8 from uint256, reverting on
               * overflow (when the input is greater than largest uint8).
               *
               * Counterpart to Solidity's `uint8` operator.
               *
               * Requirements:
               *
               * - input must fit into 8 bits
               *
               * _Available since v2.5._
               */
              function toUint8(uint256 value) internal pure returns (uint8) {
                  require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                  return uint8(value);
              }
              /**
               * @dev Converts a signed int256 into an unsigned uint256.
               *
               * Requirements:
               *
               * - input must be greater than or equal to 0.
               *
               * _Available since v3.0._
               */
              function toUint256(int256 value) internal pure returns (uint256) {
                  require(value >= 0, "SafeCast: value must be positive");
                  return uint256(value);
              }
              /**
               * @dev Returns the downcasted int248 from int256, reverting on
               * overflow (when the input is less than smallest int248 or
               * greater than largest int248).
               *
               * Counterpart to Solidity's `int248` operator.
               *
               * Requirements:
               *
               * - input must fit into 248 bits
               *
               * _Available since v4.7._
               */
              function toInt248(int256 value) internal pure returns (int248 downcasted) {
                  downcasted = int248(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
              }
              /**
               * @dev Returns the downcasted int240 from int256, reverting on
               * overflow (when the input is less than smallest int240 or
               * greater than largest int240).
               *
               * Counterpart to Solidity's `int240` operator.
               *
               * Requirements:
               *
               * - input must fit into 240 bits
               *
               * _Available since v4.7._
               */
              function toInt240(int256 value) internal pure returns (int240 downcasted) {
                  downcasted = int240(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
              }
              /**
               * @dev Returns the downcasted int232 from int256, reverting on
               * overflow (when the input is less than smallest int232 or
               * greater than largest int232).
               *
               * Counterpart to Solidity's `int232` operator.
               *
               * Requirements:
               *
               * - input must fit into 232 bits
               *
               * _Available since v4.7._
               */
              function toInt232(int256 value) internal pure returns (int232 downcasted) {
                  downcasted = int232(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
              }
              /**
               * @dev Returns the downcasted int224 from int256, reverting on
               * overflow (when the input is less than smallest int224 or
               * greater than largest int224).
               *
               * Counterpart to Solidity's `int224` operator.
               *
               * Requirements:
               *
               * - input must fit into 224 bits
               *
               * _Available since v4.7._
               */
              function toInt224(int256 value) internal pure returns (int224 downcasted) {
                  downcasted = int224(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
              }
              /**
               * @dev Returns the downcasted int216 from int256, reverting on
               * overflow (when the input is less than smallest int216 or
               * greater than largest int216).
               *
               * Counterpart to Solidity's `int216` operator.
               *
               * Requirements:
               *
               * - input must fit into 216 bits
               *
               * _Available since v4.7._
               */
              function toInt216(int256 value) internal pure returns (int216 downcasted) {
                  downcasted = int216(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
              }
              /**
               * @dev Returns the downcasted int208 from int256, reverting on
               * overflow (when the input is less than smallest int208 or
               * greater than largest int208).
               *
               * Counterpart to Solidity's `int208` operator.
               *
               * Requirements:
               *
               * - input must fit into 208 bits
               *
               * _Available since v4.7._
               */
              function toInt208(int256 value) internal pure returns (int208 downcasted) {
                  downcasted = int208(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
              }
              /**
               * @dev Returns the downcasted int200 from int256, reverting on
               * overflow (when the input is less than smallest int200 or
               * greater than largest int200).
               *
               * Counterpart to Solidity's `int200` operator.
               *
               * Requirements:
               *
               * - input must fit into 200 bits
               *
               * _Available since v4.7._
               */
              function toInt200(int256 value) internal pure returns (int200 downcasted) {
                  downcasted = int200(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
              }
              /**
               * @dev Returns the downcasted int192 from int256, reverting on
               * overflow (when the input is less than smallest int192 or
               * greater than largest int192).
               *
               * Counterpart to Solidity's `int192` operator.
               *
               * Requirements:
               *
               * - input must fit into 192 bits
               *
               * _Available since v4.7._
               */
              function toInt192(int256 value) internal pure returns (int192 downcasted) {
                  downcasted = int192(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
              }
              /**
               * @dev Returns the downcasted int184 from int256, reverting on
               * overflow (when the input is less than smallest int184 or
               * greater than largest int184).
               *
               * Counterpart to Solidity's `int184` operator.
               *
               * Requirements:
               *
               * - input must fit into 184 bits
               *
               * _Available since v4.7._
               */
              function toInt184(int256 value) internal pure returns (int184 downcasted) {
                  downcasted = int184(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
              }
              /**
               * @dev Returns the downcasted int176 from int256, reverting on
               * overflow (when the input is less than smallest int176 or
               * greater than largest int176).
               *
               * Counterpart to Solidity's `int176` operator.
               *
               * Requirements:
               *
               * - input must fit into 176 bits
               *
               * _Available since v4.7._
               */
              function toInt176(int256 value) internal pure returns (int176 downcasted) {
                  downcasted = int176(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
              }
              /**
               * @dev Returns the downcasted int168 from int256, reverting on
               * overflow (when the input is less than smallest int168 or
               * greater than largest int168).
               *
               * Counterpart to Solidity's `int168` operator.
               *
               * Requirements:
               *
               * - input must fit into 168 bits
               *
               * _Available since v4.7._
               */
              function toInt168(int256 value) internal pure returns (int168 downcasted) {
                  downcasted = int168(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
              }
              /**
               * @dev Returns the downcasted int160 from int256, reverting on
               * overflow (when the input is less than smallest int160 or
               * greater than largest int160).
               *
               * Counterpart to Solidity's `int160` operator.
               *
               * Requirements:
               *
               * - input must fit into 160 bits
               *
               * _Available since v4.7._
               */
              function toInt160(int256 value) internal pure returns (int160 downcasted) {
                  downcasted = int160(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
              }
              /**
               * @dev Returns the downcasted int152 from int256, reverting on
               * overflow (when the input is less than smallest int152 or
               * greater than largest int152).
               *
               * Counterpart to Solidity's `int152` operator.
               *
               * Requirements:
               *
               * - input must fit into 152 bits
               *
               * _Available since v4.7._
               */
              function toInt152(int256 value) internal pure returns (int152 downcasted) {
                  downcasted = int152(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
              }
              /**
               * @dev Returns the downcasted int144 from int256, reverting on
               * overflow (when the input is less than smallest int144 or
               * greater than largest int144).
               *
               * Counterpart to Solidity's `int144` operator.
               *
               * Requirements:
               *
               * - input must fit into 144 bits
               *
               * _Available since v4.7._
               */
              function toInt144(int256 value) internal pure returns (int144 downcasted) {
                  downcasted = int144(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
              }
              /**
               * @dev Returns the downcasted int136 from int256, reverting on
               * overflow (when the input is less than smallest int136 or
               * greater than largest int136).
               *
               * Counterpart to Solidity's `int136` operator.
               *
               * Requirements:
               *
               * - input must fit into 136 bits
               *
               * _Available since v4.7._
               */
              function toInt136(int256 value) internal pure returns (int136 downcasted) {
                  downcasted = int136(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
              }
              /**
               * @dev Returns the downcasted int128 from int256, reverting on
               * overflow (when the input is less than smallest int128 or
               * greater than largest int128).
               *
               * Counterpart to Solidity's `int128` operator.
               *
               * Requirements:
               *
               * - input must fit into 128 bits
               *
               * _Available since v3.1._
               */
              function toInt128(int256 value) internal pure returns (int128 downcasted) {
                  downcasted = int128(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
              }
              /**
               * @dev Returns the downcasted int120 from int256, reverting on
               * overflow (when the input is less than smallest int120 or
               * greater than largest int120).
               *
               * Counterpart to Solidity's `int120` operator.
               *
               * Requirements:
               *
               * - input must fit into 120 bits
               *
               * _Available since v4.7._
               */
              function toInt120(int256 value) internal pure returns (int120 downcasted) {
                  downcasted = int120(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
              }
              /**
               * @dev Returns the downcasted int112 from int256, reverting on
               * overflow (when the input is less than smallest int112 or
               * greater than largest int112).
               *
               * Counterpart to Solidity's `int112` operator.
               *
               * Requirements:
               *
               * - input must fit into 112 bits
               *
               * _Available since v4.7._
               */
              function toInt112(int256 value) internal pure returns (int112 downcasted) {
                  downcasted = int112(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
              }
              /**
               * @dev Returns the downcasted int104 from int256, reverting on
               * overflow (when the input is less than smallest int104 or
               * greater than largest int104).
               *
               * Counterpart to Solidity's `int104` operator.
               *
               * Requirements:
               *
               * - input must fit into 104 bits
               *
               * _Available since v4.7._
               */
              function toInt104(int256 value) internal pure returns (int104 downcasted) {
                  downcasted = int104(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
              }
              /**
               * @dev Returns the downcasted int96 from int256, reverting on
               * overflow (when the input is less than smallest int96 or
               * greater than largest int96).
               *
               * Counterpart to Solidity's `int96` operator.
               *
               * Requirements:
               *
               * - input must fit into 96 bits
               *
               * _Available since v4.7._
               */
              function toInt96(int256 value) internal pure returns (int96 downcasted) {
                  downcasted = int96(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
              }
              /**
               * @dev Returns the downcasted int88 from int256, reverting on
               * overflow (when the input is less than smallest int88 or
               * greater than largest int88).
               *
               * Counterpart to Solidity's `int88` operator.
               *
               * Requirements:
               *
               * - input must fit into 88 bits
               *
               * _Available since v4.7._
               */
              function toInt88(int256 value) internal pure returns (int88 downcasted) {
                  downcasted = int88(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
              }
              /**
               * @dev Returns the downcasted int80 from int256, reverting on
               * overflow (when the input is less than smallest int80 or
               * greater than largest int80).
               *
               * Counterpart to Solidity's `int80` operator.
               *
               * Requirements:
               *
               * - input must fit into 80 bits
               *
               * _Available since v4.7._
               */
              function toInt80(int256 value) internal pure returns (int80 downcasted) {
                  downcasted = int80(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
              }
              /**
               * @dev Returns the downcasted int72 from int256, reverting on
               * overflow (when the input is less than smallest int72 or
               * greater than largest int72).
               *
               * Counterpart to Solidity's `int72` operator.
               *
               * Requirements:
               *
               * - input must fit into 72 bits
               *
               * _Available since v4.7._
               */
              function toInt72(int256 value) internal pure returns (int72 downcasted) {
                  downcasted = int72(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
              }
              /**
               * @dev Returns the downcasted int64 from int256, reverting on
               * overflow (when the input is less than smallest int64 or
               * greater than largest int64).
               *
               * Counterpart to Solidity's `int64` operator.
               *
               * Requirements:
               *
               * - input must fit into 64 bits
               *
               * _Available since v3.1._
               */
              function toInt64(int256 value) internal pure returns (int64 downcasted) {
                  downcasted = int64(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
              }
              /**
               * @dev Returns the downcasted int56 from int256, reverting on
               * overflow (when the input is less than smallest int56 or
               * greater than largest int56).
               *
               * Counterpart to Solidity's `int56` operator.
               *
               * Requirements:
               *
               * - input must fit into 56 bits
               *
               * _Available since v4.7._
               */
              function toInt56(int256 value) internal pure returns (int56 downcasted) {
                  downcasted = int56(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
              }
              /**
               * @dev Returns the downcasted int48 from int256, reverting on
               * overflow (when the input is less than smallest int48 or
               * greater than largest int48).
               *
               * Counterpart to Solidity's `int48` operator.
               *
               * Requirements:
               *
               * - input must fit into 48 bits
               *
               * _Available since v4.7._
               */
              function toInt48(int256 value) internal pure returns (int48 downcasted) {
                  downcasted = int48(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
              }
              /**
               * @dev Returns the downcasted int40 from int256, reverting on
               * overflow (when the input is less than smallest int40 or
               * greater than largest int40).
               *
               * Counterpart to Solidity's `int40` operator.
               *
               * Requirements:
               *
               * - input must fit into 40 bits
               *
               * _Available since v4.7._
               */
              function toInt40(int256 value) internal pure returns (int40 downcasted) {
                  downcasted = int40(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
              }
              /**
               * @dev Returns the downcasted int32 from int256, reverting on
               * overflow (when the input is less than smallest int32 or
               * greater than largest int32).
               *
               * Counterpart to Solidity's `int32` operator.
               *
               * Requirements:
               *
               * - input must fit into 32 bits
               *
               * _Available since v3.1._
               */
              function toInt32(int256 value) internal pure returns (int32 downcasted) {
                  downcasted = int32(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
              }
              /**
               * @dev Returns the downcasted int24 from int256, reverting on
               * overflow (when the input is less than smallest int24 or
               * greater than largest int24).
               *
               * Counterpart to Solidity's `int24` operator.
               *
               * Requirements:
               *
               * - input must fit into 24 bits
               *
               * _Available since v4.7._
               */
              function toInt24(int256 value) internal pure returns (int24 downcasted) {
                  downcasted = int24(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
              }
              /**
               * @dev Returns the downcasted int16 from int256, reverting on
               * overflow (when the input is less than smallest int16 or
               * greater than largest int16).
               *
               * Counterpart to Solidity's `int16` operator.
               *
               * Requirements:
               *
               * - input must fit into 16 bits
               *
               * _Available since v3.1._
               */
              function toInt16(int256 value) internal pure returns (int16 downcasted) {
                  downcasted = int16(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
              }
              /**
               * @dev Returns the downcasted int8 from int256, reverting on
               * overflow (when the input is less than smallest int8 or
               * greater than largest int8).
               *
               * Counterpart to Solidity's `int8` operator.
               *
               * Requirements:
               *
               * - input must fit into 8 bits
               *
               * _Available since v3.1._
               */
              function toInt8(int256 value) internal pure returns (int8 downcasted) {
                  downcasted = int8(value);
                  require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
              }
              /**
               * @dev Converts an unsigned uint256 into a signed int256.
               *
               * Requirements:
               *
               * - input must be less than or equal to maxInt256.
               *
               * _Available since v3.0._
               */
              function toInt256(uint256 value) internal pure returns (int256) {
                  // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                  require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                  return int256(value);
              }
          }
          pragma solidity ^0.8.0;
          // SPDX-License-Identifier: MIT
          library UncheckedMath {
              function uncheckedInc(uint256 _number) internal pure returns (uint256) {
                  unchecked {
                      return _number + 1;
                  }
              }
              function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
                  unchecked {
                      return _lhs + _rhs;
                  }
              }
          }
          pragma solidity ^0.8.0;
          // SPDX-License-Identifier: MIT
          import "./libraries/Diamond.sol";
          /// @title Diamond Proxy Contract (EIP-2535)
          /// @author Matter Labs
          contract DiamondProxy {
              constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) {
                  // Check that the contract is deployed on the expected chain.
                  // Thus, the contract deployed by the same Create2 factory on the different chain will have different addresses!
                  require(_chainId == block.chainid, "pr");
                  Diamond.diamondCut(_diamondCut);
              }
              /// @dev 1. Find the facet for the function that is called.
              /// @dev 2. Delegate the execution to the found facet via `delegatecall`.
              fallback() external payable {
                  Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
                  // Check whether the data contains a "full" selector or it is empty.
                  // Required because Diamond proxy finds a facet by function signature,
                  // which is not defined for data length in range [1, 3].
                  require(msg.data.length >= 4 || msg.data.length == 0, "Ut");
                  // Get facet from function selector
                  Diamond.SelectorToFacet memory facet = diamondStorage.selectorToFacet[msg.sig];
                  address facetAddress = facet.facetAddress;
                  require(facetAddress != address(0), "F"); // Proxy has no facet for this selector
                  require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
                  assembly {
                      // The pointer to the free memory slot
                      let ptr := mload(0x40)
                      // Copy function signature and arguments from calldata at zero position into memory at pointer position
                      calldatacopy(ptr, 0, calldatasize())
                      // Delegatecall method of the implementation contract returns 0 on error
                      let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
                      // Get the size of the last return data
                      let size := returndatasize()
                      // Copy the size length of bytes from return data at zero position to pointer position
                      returndatacopy(ptr, 0, size)
                      // Depending on the result value
                      switch result
                      case 0 {
                          // End execution and revert state changes
                          revert(ptr, size)
                      }
                      default {
                          // Return data with length of size at pointers position
                          return(ptr, size)
                      }
                  }
              }
          }
          pragma solidity ^0.8.0;
          // SPDX-License-Identifier: MIT
          import "@openzeppelin/contracts/utils/math/SafeCast.sol";
          import "../../common/libraries/UncheckedMath.sol";
          /// @author Matter Labs
          /// @notice The helper library for managing the EIP-2535 diamond proxy.
          library Diamond {
              using UncheckedMath for uint256;
              using SafeCast for uint256;
              /// @dev Magic value that should be returned by diamond cut initialize contracts.
              /// @dev Used to distinguish calls to contracts that were supposed to be used as diamond initializer from other contracts.
              bytes32 constant DIAMOND_INIT_SUCCESS_RETURN_VALUE =
                  0x33774e659306e47509050e97cb651e731180a42d458212294d30751925c551a2; // keccak256("diamond.zksync.init") - 1
              /// @dev Storage position of `DiamondStorage` structure.
              bytes32 constant DIAMOND_STORAGE_POSITION = 0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b; // keccak256("diamond.standard.diamond.storage") - 1;
              event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata);
              /// @dev Utility struct that contains associated facet & meta information of selector
              /// @param facetAddress address of the facet which is connected with selector
              /// @param selectorPosition index in `FacetToSelectors.selectors` array, where is selector stored
              /// @param isFreezable denotes whether the selector can be frozen.
              struct SelectorToFacet {
                  address facetAddress;
                  uint16 selectorPosition;
                  bool isFreezable;
              }
              /// @dev Utility struct that contains associated selectors & meta information of facet
              /// @param selectors list of all selectors that belong to the facet
              /// @param facetPosition index in `DiamondStorage.facets` array, where is facet stored
              struct FacetToSelectors {
                  bytes4[] selectors;
                  uint16 facetPosition;
              }
              /// @notice The structure that holds all diamond proxy associated parameters
              /// @dev According to the EIP-2535 should be stored on a special storage key - `DIAMOND_STORAGE_POSITION`
              /// @param selectorToFacet A mapping from the selector to the facet address and its meta information
              /// @param facetToSelectors A mapping from facet address to its selector with meta information
              /// @param facets The array of all unique facet addresses that belong to the diamond proxy
              /// @param isFrozen Denotes whether the diamond proxy is frozen and all freezable facets are not accessible
              struct DiamondStorage {
                  mapping(bytes4 => SelectorToFacet) selectorToFacet;
                  mapping(address => FacetToSelectors) facetToSelectors;
                  address[] facets;
                  bool isFrozen;
              }
              /// @dev Parameters for diamond changes that touch one of the facets
              /// @param facet The address of facet that's affected by the cut
              /// @param action The action that is made on the facet
              /// @param isFreezable Denotes whether the facet & all their selectors can be frozen
              /// @param selectors An array of unique selectors that belongs to the facet address
              struct FacetCut {
                  address facet;
                  Action action;
                  bool isFreezable;
                  bytes4[] selectors;
              }
              /// @dev Structure of the diamond proxy changes
              /// @param facetCuts The set of changes (adding/removing/replacement) of implementation contracts
              /// @param initAddress The address that's delegate called after setting up new facet changes
              /// @param initCalldata Calldata for the delegate call to `initAddress`
              struct DiamondCutData {
                  FacetCut[] facetCuts;
                  address initAddress;
                  bytes initCalldata;
              }
              /// @dev Type of change over diamond: add/replace/remove facets
              enum Action {
                  Add,
                  Replace,
                  Remove
              }
              /// @return diamondStorage The pointer to the storage where all specific diamond proxy parameters stored
              function getDiamondStorage() internal pure returns (DiamondStorage storage diamondStorage) {
                  bytes32 position = DIAMOND_STORAGE_POSITION;
                  assembly {
                      diamondStorage.slot := position
                  }
              }
              /// @dev Add/replace/remove any number of selectors and optionally execute a function with delegatecall
              /// @param _diamondCut Diamond's facet changes and the parameters to optional initialization delegatecall
              function diamondCut(DiamondCutData memory _diamondCut) internal {
                  FacetCut[] memory facetCuts = _diamondCut.facetCuts;
                  address initAddress = _diamondCut.initAddress;
                  bytes memory initCalldata = _diamondCut.initCalldata;
                  uint256 facetCutsLength = facetCuts.length;
                  for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) {
                      Action action = facetCuts[i].action;
                      address facet = facetCuts[i].facet;
                      bool isFacetFreezable = facetCuts[i].isFreezable;
                      bytes4[] memory selectors = facetCuts[i].selectors;
                      require(selectors.length > 0, "B"); // no functions for diamond cut
                      if (action == Action.Add) {
                          _addFunctions(facet, selectors, isFacetFreezable);
                      } else if (action == Action.Replace) {
                          _replaceFunctions(facet, selectors, isFacetFreezable);
                      } else if (action == Action.Remove) {
                          _removeFunctions(facet, selectors);
                      } else {
                          revert("C"); // undefined diamond cut action
                      }
                  }
                  _initializeDiamondCut(initAddress, initCalldata);
                  emit DiamondCut(facetCuts, initAddress, initCalldata);
              }
              /// @dev Add new functions to the diamond proxy
              /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
              function _addFunctions(
                  address _facet,
                  bytes4[] memory _selectors,
                  bool _isFacetFreezable
              ) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  require(_facet != address(0), "G"); // facet with zero address cannot be added
                  // Add facet to the list of facets if the facet address is new one
                  _saveFacetIfNew(_facet);
                  uint256 selectorsLength = _selectors.length;
                  for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                      bytes4 selector = _selectors[i];
                      SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                      require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists
                      _addOneFunction(_facet, selector, _isFacetFreezable);
                  }
              }
              /// @dev Change associated facets to already known function selectors
              /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
              function _replaceFunctions(
                  address _facet,
                  bytes4[] memory _selectors,
                  bool _isFacetFreezable
              ) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  require(_facet != address(0), "K"); // cannot replace facet with zero address
                  uint256 selectorsLength = _selectors.length;
                  for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                      bytes4 selector = _selectors[i];
                      SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                      require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address
                      _removeOneFunction(oldFacet.facetAddress, selector);
                      // Add facet to the list of facets if the facet address is a new one
                      _saveFacetIfNew(_facet);
                      _addOneFunction(_facet, selector, _isFacetFreezable);
                  }
              }
              /// @dev Remove association with function and facet
              /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
              function _removeFunctions(address _facet, bytes4[] memory _selectors) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  require(_facet == address(0), "a1"); // facet address must be zero
                  uint256 selectorsLength = _selectors.length;
                  for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                      bytes4 selector = _selectors[i];
                      SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                      require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet
                      _removeOneFunction(oldFacet.facetAddress, selector);
                  }
              }
              /// @dev Add address to the list of known facets if it is not on the list yet
              /// NOTE: should be called ONLY before adding a new selector associated with the address
              function _saveFacetIfNew(address _facet) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  uint256 selectorsLength = ds.facetToSelectors[_facet].selectors.length;
                  // If there are no selectors associated with facet then save facet as new one
                  if (selectorsLength == 0) {
                      ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16();
                      ds.facets.push(_facet);
                  }
              }
              /// @dev Add one function to the already known facet
              /// NOTE: It is expected but NOT enforced that:
              /// - `_facet` is NON-ZERO address
              /// - `_facet` is already stored address in `DiamondStorage.facets`
              /// - `_selector` is NOT associated by another facet
              function _addOneFunction(
                  address _facet,
                  bytes4 _selector,
                  bool _isSelectorFreezable
              ) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16();
                  // if selectorPosition is nonzero, it means it is not a new facet
                  // so the freezability of the first selector must be matched to _isSelectorFreezable
                  // so all the selectors in a facet will have the same freezability
                  if (selectorPosition != 0) {
                      bytes4 selector0 = ds.facetToSelectors[_facet].selectors[0];
                      require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1");
                  }
                  ds.selectorToFacet[_selector] = SelectorToFacet({
                      facetAddress: _facet,
                      selectorPosition: selectorPosition,
                      isFreezable: _isSelectorFreezable
                  });
                  ds.facetToSelectors[_facet].selectors.push(_selector);
              }
              /// @dev Remove one associated function with facet
              /// NOTE: It is expected but NOT enforced that `_facet` is NON-ZERO address
              function _removeOneFunction(address _facet, bytes4 _selector) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  // Get index of `FacetToSelectors.selectors` of the selector and last element of array
                  uint256 selectorPosition = ds.selectorToFacet[_selector].selectorPosition;
                  uint256 lastSelectorPosition = ds.facetToSelectors[_facet].selectors.length - 1;
                  // If the selector is not at the end of the array then move the last element to the selector position
                  if (selectorPosition != lastSelectorPosition) {
                      bytes4 lastSelector = ds.facetToSelectors[_facet].selectors[lastSelectorPosition];
                      ds.facetToSelectors[_facet].selectors[selectorPosition] = lastSelector;
                      ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16();
                  }
                  // Remove last element from the selectors array
                  ds.facetToSelectors[_facet].selectors.pop();
                  // Finally, clean up the association with facet
                  delete ds.selectorToFacet[_selector];
                  // If there are no selectors for facet then remove the facet from the list of known facets
                  if (lastSelectorPosition == 0) {
                      _removeFacet(_facet);
                  }
              }
              /// @dev remove facet from the list of known facets
              /// NOTE: It is expected but NOT enforced that there are no selectors associated with `_facet`
              function _removeFacet(address _facet) private {
                  DiamondStorage storage ds = getDiamondStorage();
                  // Get index of `DiamondStorage.facets` of the facet and last element of array
                  uint256 facetPosition = ds.facetToSelectors[_facet].facetPosition;
                  uint256 lastFacetPosition = ds.facets.length - 1;
                  // If the facet is not at the end of the array then move the last element to the facet position
                  if (facetPosition != lastFacetPosition) {
                      address lastFacet = ds.facets[lastFacetPosition];
                      ds.facets[facetPosition] = lastFacet;
                      ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16();
                  }
                  // Remove last element from the facets array
                  ds.facets.pop();
              }
              /// @dev Delegates call to the initialization address with provided calldata
              /// @dev Used as a final step of diamond cut to execute the logic of the initialization for changed facets
              function _initializeDiamondCut(address _init, bytes memory _calldata) private {
                  if (_init == address(0)) {
                      require(_calldata.length == 0, "H"); // Non-empty calldata for zero address
                  } else {
                      // Do not check whether `_init` is a contract since later we check that it returns data.
                      (bool success, bytes memory data) = _init.delegatecall(_calldata);
                      require(success, "I"); // delegatecall failed
                      // Check that called contract returns magic value to make sure that contract logic
                      // supposed to be used as diamond cut initializer.
                      require(data.length == 32, "lp");
                      require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1");
                  }
              }
          }
          

          File 3 of 4: ExecutorFacet
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./interfaces/IAllowList.sol";
          /// @author Matter Labs
          abstract contract AllowListed {
              modifier senderCanCallFunction(IAllowList _allowList) {
                  // Preventing the stack too deep error
                  {
                      require(_allowList.canCall(msg.sender, address(this), msg.sig), "nr");
                  }
                  _;
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          interface IAllowList {
              /*//////////////////////////////////////////////////////////////
                                      EVENTS
              //////////////////////////////////////////////////////////////*/
              /// @notice Access mode of target contract is changed
              event UpdateAccessMode(address indexed target, AccessMode previousMode, AccessMode newMode);
              /// @notice Permission to call is changed
              event UpdateCallPermission(address indexed caller, address indexed target, bytes4 indexed functionSig, bool status);
              /// @notice Type of access to a specific contract includes three different modes
              /// @param Closed No one has access to the contract
              /// @param SpecialAccessOnly Any address with granted special access can interact with a contract (see `hasSpecialAccessToCall`)
              /// @param Public Everyone can interact with a contract
              enum AccessMode {
                  Closed,
                  SpecialAccessOnly,
                  Public
              }
              /// @dev A struct that contains deposit limit data of a token
              /// @param depositLimitation Whether any deposit limitation is placed or not
              /// @param depositCap The maximum amount that can be deposited.
              struct Deposit {
                  bool depositLimitation;
                  uint256 depositCap;
              }
              /*//////////////////////////////////////////////////////////////
                                      GETTERS
              //////////////////////////////////////////////////////////////*/
              function getAccessMode(address _target) external view returns (AccessMode);
              function hasSpecialAccessToCall(
                  address _caller,
                  address _target,
                  bytes4 _functionSig
              ) external view returns (bool);
              function canCall(
                  address _caller,
                  address _target,
                  bytes4 _functionSig
              ) external view returns (bool);
              function getTokenDepositLimitData(address _l1Token) external view returns (Deposit memory);
              /*//////////////////////////////////////////////////////////////
                                     ALLOW LIST LOGIC
              //////////////////////////////////////////////////////////////*/
              function setBatchAccessMode(address[] calldata _targets, AccessMode[] calldata _accessMode) external;
              function setAccessMode(address _target, AccessMode _accessMode) external;
              function setBatchPermissionToCall(
                  address[] calldata _callers,
                  address[] calldata _targets,
                  bytes4[] calldata _functionSigs,
                  bool[] calldata _enables
              ) external;
              function setPermissionToCall(
                  address _caller,
                  address _target,
                  bytes4 _functionSig,
                  bool _enable
              ) external;
              /*//////////////////////////////////////////////////////////////
                                     DEPOSIT LIMIT LOGIC
              //////////////////////////////////////////////////////////////*/
              function setDepositLimit(
                  address _l1Token,
                  bool _depositLimitation,
                  uint256 _depositCap
              ) external;
          }
          pragma solidity ^0.8.0;
          // SPDX-License-Identifier: MIT
          /// @dev The address of the L2 deployer system contract.
          address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006);
          /// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed bytecode.
          /// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address.
          /// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer system contract
          /// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the `diamond-initializers` contracts.
          address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007);
          /// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
          address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008);
          /// @dev The formal address of the initial program of the system: the bootloader
          address constant L2_BOOTLOADER_ADDRESS = address(0x8001);
          /// @dev The address of the eth token system contract
          address constant L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a);
          /// @dev The address of the known code storage system contract
          address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004);
          /// @dev The address of the context system contract
          address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b);
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          /**
           * @author Matter Labs
           * @notice Helper library for working with L2 contracts on L1.
           */
          library L2ContractHelper {
              /// @dev The prefix used to create CREATE2 addresses.
              bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2");
              /// @notice Validate the bytecode format and calculate its hash.
              /// @param _bytecode The bytecode to hash.
              /// @return hashedBytecode The 32-byte hash of the bytecode.
              /// Note: The function reverts the execution if the bytecode has non expected format:
              /// - Bytecode bytes length is not a multiple of 32
              /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
              /// - Bytecode words length is not odd
              function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) {
                  // Note that the length of the bytecode must be provided in 32-byte words.
                  require(_bytecode.length % 32 == 0, "bl");
                  uint256 bytecodeLenInWords = _bytecode.length / 32;
                  require(bytecodeLenInWords < 2**16, "pp"); // bytecode length must be less than 2^16 words
                  require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd
                  hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
                  // Setting the version of the hash
                  hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
                  // Setting the length
                  hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
              }
              /// @notice Validates the format of the given bytecode hash.
              /// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash.
              /// @dev The function reverts on invalid bytecode hash formam.
              /// @param _bytecodeHash The hash of the bytecode to validate.
              function validateBytecodeHash(bytes32 _bytecodeHash) internal pure {
                  uint8 version = uint8(_bytecodeHash[0]);
                  require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash
                  require(_bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd
              }
              /// @notice Returns the length of the bytecode associated with the given hash.
              /// @param _bytecodeHash The hash of the bytecode.
              /// @return codeLengthInWords The length of the bytecode in words.
              function _bytecodeLen(bytes32 _bytecodeHash) private pure returns (uint256 codeLengthInWords) {
                  codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3]));
              }
              /// @notice Computes the create2 address for a Layer 2 contract.
              /// @param _sender The address of the sender.
              /// @param _salt The salt value to use in the create2 address computation.
              /// @param _bytecodeHash The contract bytecode hash.
              /// @param _constructorInputHash The hash of the constructor input data.
              /// @return The create2 address of the contract.
              /// NOTE: L2 create2 derivation is different from L1 derivation!
              function computeCreate2Address(
                  address _sender,
                  bytes32 _salt,
                  bytes32 _bytecodeHash,
                  bytes32 _constructorInputHash
              ) internal pure returns (address) {
                  bytes32 senderBytes = bytes32(uint256(uint160(_sender)));
                  bytes32 data = keccak256(
                      bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash)
                  );
                  return address(uint160(uint256(data)));
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          library UncheckedMath {
              function uncheckedInc(uint256 _number) internal pure returns (uint256) {
                  unchecked {
                      return _number + 1;
                  }
              }
              function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
                  unchecked {
                      return _lhs + _rhs;
                  }
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          /**
           * @author Matter Labs
           * @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array.
           * @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type.
           *
           * @dev WARNING!
           * 1) Functions don't check the length of the bytes array, so it can go out of bounds.
           * The user of the library must check for bytes length before using any functions from the library!
           *
           * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html.
           * Using data in inline assembly can lead to unexpected behavior!
           */
          library UnsafeBytes {
              function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) {
                  assembly {
                      offset := add(_start, 4)
                      result := mload(add(_bytes, offset))
                  }
              }
              function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) {
                  assembly {
                      offset := add(_start, 20)
                      result := mload(add(_bytes, offset))
                  }
              }
              function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) {
                  assembly {
                      offset := add(_start, 32)
                      result := mload(add(_bytes, offset))
                  }
              }
              function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) {
                  assembly {
                      offset := add(_start, 32)
                      result := mload(add(_bytes, offset))
                  }
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          /**
           * @dev Contract module that helps prevent reentrant calls to a function.
           *
           * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
           * available, which can be applied to functions to make sure there are no nested
           * (reentrant) calls to them.
           *
           * Note that because there is a single `nonReentrant` guard, functions marked as
           * `nonReentrant` may not call one another. This can be worked around by making
           * those functions `private`, and then adding `external` `nonReentrant` entry
           * points to them.
           *
           * TIP: If you would like to learn more about reentrancy and alternative ways
           * to protect against it, check out our blog post
           * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
           *
           * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
           * metering changes introduced in the Istanbul hardfork.
           */
          abstract contract ReentrancyGuard {
              /// @dev Address of lock flag variable.
              /// @dev Flag is placed at random memory location to not interfere with Storage contract.
              uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1;
              // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/566a774222707e424896c0c390a84dc3c13bdcb2/contracts/security/ReentrancyGuard.sol
              // The values being non-zero value makes deployment a bit more expensive,
              // but in exchange the refund on every call to nonReentrant will be lower in
              // amount. Since refunds are capped to a percentage of the total
              // transaction's gas, it is best to keep them low in cases like this one, to
              // increase the likelihood of the full refund coming into effect.
              uint256 private constant _NOT_ENTERED = 1;
              uint256 private constant _ENTERED = 2;
              modifier reentrancyGuardInitializer() {
                  _initializeReentrancyGuard();
                  _;
              }
              function _initializeReentrancyGuard() private {
                  uint256 lockSlotOldValue;
                  // Storing an initial non-zero value makes deployment a bit more
                  // expensive but in exchange every call to nonReentrant
                  // will be cheaper.
                  assembly {
                      lockSlotOldValue := sload(LOCK_FLAG_ADDRESS)
                      sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
                  }
                  // Check that storage slot for reentrancy guard is empty to rule out possibility of slot conflict
                  require(lockSlotOldValue == 0, "1B");
              }
              /**
               * @dev Prevents a contract from calling itself, directly or indirectly.
               * Calling a `nonReentrant` function from another `nonReentrant`
               * function is not supported. It is possible to prevent this from happening
               * by making the `nonReentrant` function external, and make it call a
               * `private` function that does the actual work.
               */
              modifier nonReentrant() {
                  uint256 _status;
                  assembly {
                      _status := sload(LOCK_FLAG_ADDRESS)
                  }
                  // On the first call to nonReentrant, _notEntered will be true
                  require(_status == _NOT_ENTERED, "r1");
                  // Any calls to nonReentrant after this point will fail
                  assembly {
                      sstore(LOCK_FLAG_ADDRESS, _ENTERED)
                  }
                  _;
                  // By storing the original value once again, a refund is triggered (see
                  // https://eips.ethereum.org/EIPS/eip-2200)
                  assembly {
                      sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
                  }
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          /// @dev `keccak256("")`
          bytes32 constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
          /// @dev Bytes in raw L2 log
          /// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBlock, address sender, bytes32 key, bytes32 value)
          uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88;
          /// @dev The maximum length of the bytes array with L2 -> L1 logs
          uint256 constant MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512;
          /// @dev L2 -> L1 logs Merkle tree height
          uint256 constant L2_TO_L1_LOG_MERKLE_TREE_HEIGHT = 9;
          /// @dev The value of default leaf hash for L2 -> L1 logs Merkle tree
          /// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree
          /// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))`
          bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba;
          /// @dev Number of bytes in a one initial storage change
          /// @dev Equal to the bytes size of the tuple - (bytes32 key, bytes32 value)
          uint256 constant INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE = 64;
          /// @dev The maximum length of the bytes array with initial storage changes
          uint256 constant MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE * 4765;
          /// @dev Number of bytes in a one repeated storage change
          /// @dev Equal to the bytes size of the tuple - (bytes8 key, bytes32 value)
          uint256 constant REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE = 40;
          /// @dev The maximum length of the bytes array with repeated storage changes
          uint256 constant MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE * 7564;
          // TODO: change constant to the real root hash of empty Merkle tree (SMA-184)
          bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = bytes32(0);
          /// @dev Denotes the type of the zkSync transaction that came from L1.
          uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255;
          /// @dev Denotes the type of the zkSync transaction that is used for system upgrades.
          uint256 constant SYSTEM_UPGRADE_L2_TX_TYPE = 254;
          /// @dev The amount of time in seconds the validator has to process the priority transaction
          /// NOTE: The constant is set to zero for the Alpha release period
          uint256 constant PRIORITY_EXPIRATION = 0 days;
          /// @dev Notice period before activation preparation status of upgrade mode (in seconds)
          /// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it.
          uint256 constant UPGRADE_NOTICE_PERIOD = 0;
          /// @dev Timestamp - seconds since unix epoch
          uint256 constant COMMIT_TIMESTAMP_NOT_OLDER = 365 days;
          /// @dev Maximum available error between real commit block timestamp and analog used in the verifier (in seconds)
          /// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 15 seconds)
          uint256 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 365 days;
          /// @dev Bit mask to apply for verifier public input before verifying.
          uint256 constant INPUT_MASK = 452312848583266388373324160190187140051835877600158453279131187530910662655;
          /// @dev The maximum number of L2 gas that a user can request for an L2 transaction
          uint256 constant L2_TX_MAX_GAS_LIMIT = 80000000;
          /// @dev The maximum number of the pubdata an L2 operation should be allowed to use.
          uint256 constant MAX_PUBDATA_PER_BLOCK = 110000;
          /// @dev The maximum number of the pubdata an priority operation should be allowed to use.
          /// For now, it is somewhat lower than the maximum number of pubdata allowed for an L2 transaction,
          /// to ensure that the transaction is definitely processable on L2 despite any potential overhead.
          uint256 constant PRIORITY_TX_MAX_PUBDATA = 99000;
          /// @dev The default price per L2 gas to be used for L1->L2 transactions
          uint256 constant FAIR_L2_GAS_PRICE = 500000000;
          /// @dev Even though the price for 1 byte of pubdata is 16 L1 gas, we have a slightly increased
          /// value.
          uint256 constant L1_GAS_PER_PUBDATA_BYTE = 17;
          /// @dev The computational overhead of processing an L2 block.
          uint256 constant BLOCK_OVERHEAD_L2_GAS = 1200000;
          /// @dev The overhead in L1 gas of interacting with the L1
          uint256 constant BLOCK_OVERHEAD_L1_GAS = 1000000;
          /// @dev The equivalent in L1 pubdata of L1 gas used for working with L1
          uint256 constant BLOCK_OVERHEAD_PUBDATA = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE;
          /// @dev The maximum number of transactions in L2 block:
          uint256 constant MAX_TRANSACTIONS_IN_BLOCK = 1024;
          /// @dev The size of the bootloader memory dedicated to the encodings of transactions
          uint256 constant BOOTLOADER_TX_ENCODING_SPACE = 485225;
          /// @dev The intrinsic cost of the L1->l2 transaction in computational L2 gas
          uint256 constant L1_TX_INTRINSIC_L2_GAS = 167157;
          /// @dev The intrinsic cost of the L1->l2 transaction in pubdata
          uint256 constant L1_TX_INTRINSIC_PUBDATA = 88;
          /// @dev The minimal base price for L1 transaction
          uint256 constant L1_TX_MIN_L2_GAS_BASE = 173484;
          /// @dev The number of L2 gas the transaction starts costing more with each 544 bytes of encoding
          uint256 constant L1_TX_DELTA_544_ENCODING_BYTES = 1656;
          /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
          uint256 constant L1_TX_DELTA_FACTORY_DEPS_L2_GAS = 2473;
          /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
          uint256 constant L1_TX_DELTA_FACTORY_DEPS_PUBDATA = 64;
          /// @dev The number of pubdata an L1->L2 transaction requires with each new factory dependency
          uint256 constant MAX_NEW_FACTORY_DEPS = 32;
          /// @dev The L2 gasPricePerPubdata required to be used in bridges.
          uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800;
          /// @dev The mask which should be applied to the packed batch and L2 block timestamp in order
          /// to obtain the L2 block timestamp. Applying this mask is equivalent to calculating modulo 2**128
          uint256 constant PACKED_L2_BLOCK_TIMESTAMP_MASK = 0xffffffffffffffffffffffffffffffff;
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "../Storage.sol";
          import "../../common/ReentrancyGuard.sol";
          import "../../common/AllowListed.sol";
          /// @title Base contract containing functions accessible to the other facets.
          /// @author Matter Labs
          contract Base is ReentrancyGuard, AllowListed {
              AppStorage internal s;
              /// @notice Checks that the message sender is an active governor
              modifier onlyGovernor() {
                  require(msg.sender == s.governor, "1g"); // only by governor
                  _;
              }
              /// @notice Checks if validator is active
              modifier onlyValidator() {
                  require(s.validators[msg.sender], "1h"); // validator is not active
                  _;
              }
              /// @notice Checks if `msg.sender` is the security council
              modifier onlySecurityCouncil() {
                  require(msg.sender == s.upgrades.securityCouncil, "a9"); // not a security council
                  _;
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import {Base} from "./Base.sol";
          import {COMMIT_TIMESTAMP_NOT_OLDER, COMMIT_TIMESTAMP_APPROXIMATION_DELTA, EMPTY_STRING_KECCAK, L2_TO_L1_LOG_SERIALIZE_SIZE, INPUT_MASK, MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, PACKED_L2_BLOCK_TIMESTAMP_MASK} from "../Config.sol";
          import {IExecutor} from "../interfaces/IExecutor.sol";
          import {PairingsBn254} from "../libraries/PairingsBn254.sol";
          import {PriorityQueue, PriorityOperation} from "../libraries/PriorityQueue.sol";
          import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol";
          import {UnsafeBytes} from "../../common/libraries/UnsafeBytes.sol";
          import {L2ContractHelper} from "../../common/libraries/L2ContractHelper.sol";
          import {VerifierParams} from "../Storage.sol";
          import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR} from "../../common/L2ContractAddresses.sol";
          /// @title zkSync Executor contract capable of processing events emitted in the zkSync protocol.
          /// @author Matter Labs
          contract ExecutorFacet is Base, IExecutor {
              using UncheckedMath for uint256;
              using PriorityQueue for PriorityQueue.Queue;
              string public constant override getName = "ExecutorFacet";
              /// @dev Process one block commit using the previous block StoredBlockInfo
              /// @dev returns new block StoredBlockInfo
              /// @notice Does not change storage
              function _commitOneBlock(
                  StoredBlockInfo memory _previousBlock,
                  CommitBlockInfo calldata _newBlock,
                  bytes32 _expectedSystemContractUpgradeTxHash
              ) internal view returns (StoredBlockInfo memory) {
                  require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block
                  // Check that block contain all meta information for L2 logs.
                  // Get the chained hash of priority transaction hashes.
                  (
                      uint256 expectedNumberOfLayer1Txs,
                      bytes32 expectedPriorityOperationsHash,
                      bytes32 previousBlockHash,
                      uint256 packedBatchAndL2BlockTimestamp
                  ) = _processL2Logs(_newBlock, _expectedSystemContractUpgradeTxHash);
                  require(_previousBlock.blockHash == previousBlockHash, "l");
                  // Check that the priority operation hash in the L2 logs is as expected
                  require(expectedPriorityOperationsHash == _newBlock.priorityOperationsHash, "t");
                  // Check that the number of processed priority operations is as expected
                  require(expectedNumberOfLayer1Txs == _newBlock.numberOfLayer1Txs, "ta");
                  // Check the timestamp of the new block
                  _verifyBlockTimestamp(packedBatchAndL2BlockTimestamp, _newBlock.timestamp, _previousBlock.timestamp);
                  // Preventing "stack too deep error"
                  {
                      // Check the index of repeated storage writes
                      uint256 newStorageChangesIndexes = uint256(uint32(bytes4(_newBlock.initialStorageChanges[:4])));
                      require(
                          _previousBlock.indexRepeatedStorageChanges + newStorageChangesIndexes ==
                              _newBlock.indexRepeatedStorageChanges,
                          "yq"
                      );
                  }
                  // Create block commitment for the proof verification
                  bytes32 commitment = _createBlockCommitment(_newBlock);
                  return
                      StoredBlockInfo(
                          _newBlock.blockNumber,
                          _newBlock.newStateRoot,
                          _newBlock.indexRepeatedStorageChanges,
                          _newBlock.numberOfLayer1Txs,
                          _newBlock.priorityOperationsHash,
                          _newBlock.l2LogsTreeRoot,
                          _newBlock.timestamp,
                          commitment
                      );
              }
              /// @notice checks that the timestamps of both the new batch and the new L2 block are correct.
              /// @param _packedBatchAndL2BlockTimestamp - packed batch and L2 block timestamp in a foramt of batchTimestamp * 2**128 + l2BlockTimestamp
              /// @param _expectedBatchTimestamp - expected batch timestamp
              /// @param _previousBatchTimestamp - the timestamp of the previous batch
              function _verifyBlockTimestamp(
                  uint256 _packedBatchAndL2BlockTimestamp,
                  uint256 _expectedBatchTimestamp,
                  uint256 _previousBatchTimestamp
              ) internal view {
                  // Check that the timestamp that came from the system context is expected
                  uint256 batchTimestamp = _packedBatchAndL2BlockTimestamp >> 128;
                  require(batchTimestamp == _expectedBatchTimestamp, "tb");
                  // While the fact that _previousBatchTimestamp < batchTimestamp is already checked on L2,
                  // we double check it here for clarity
                  require(_previousBatchTimestamp < batchTimestamp, "h");
                  uint256 lastL2BlockTimestamp = _packedBatchAndL2BlockTimestamp & PACKED_L2_BLOCK_TIMESTAMP_MASK;
                  // On L2, all blocks have timestamps within the range of [batchTimestamp, lastL2BlockTimestamp].
                  // So here we need to only double check that:
                  // - The timestamp of the batch is not too small.
                  // - The timestamp of the last L2 block is not too big.
                  require(block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= batchTimestamp, "h1"); // New batch timestamp is too small
                  require(lastL2BlockTimestamp <= block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA, "h2"); // The last L2 block timestamp is too big
              }
              /// @dev Check that L2 logs are proper and block contain all meta information for them
              function _processL2Logs(CommitBlockInfo calldata _newBlock, bytes32 _expectedSystemContractUpgradeTxHash)
                  internal
                  pure
                  returns (
                      uint256 numberOfLayer1Txs,
                      bytes32 chainedPriorityTxsHash,
                      bytes32 previousBlockHash,
                      uint256 packedBatchAndL2BlockTimestamp
                  )
              {
                  // Copy L2 to L1 logs into memory.
                  bytes memory emittedL2Logs = _newBlock.l2Logs[4:];
                  uint256 currentMessage;
                  // Auxiliary variable that is needed to enforce that `previousBlockHash` and `blockTimestamp` was read exactly one time
                  bool isSystemContextLogProcessed;
                  bytes[] calldata factoryDeps = _newBlock.factoryDeps;
                  uint256 currentBytecode;
                  chainedPriorityTxsHash = EMPTY_STRING_KECCAK;
                  // linear traversal of the logs
                  for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {
                      (address logSender, ) = UnsafeBytes.readAddress(emittedL2Logs, i + 4);
                      // show preimage for hashed message stored in log
                      if (logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) {
                          (bytes32 hashedMessage, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56);
                          require(keccak256(_newBlock.l2ArbitraryLengthMessages[currentMessage]) == hashedMessage, "k2");
                          currentMessage = currentMessage.uncheckedInc();
                      } else if (logSender == L2_BOOTLOADER_ADDRESS) {
                          (bytes32 canonicalTxHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24);
                          if (_expectedSystemContractUpgradeTxHash != bytes32(0)) {
                              require(_expectedSystemContractUpgradeTxHash == canonicalTxHash, "bz");
                              _expectedSystemContractUpgradeTxHash = bytes32(0);
                          } else {
                              chainedPriorityTxsHash = keccak256(abi.encode(chainedPriorityTxsHash, canonicalTxHash));
                              // Overflow is not realistic
                              numberOfLayer1Txs = numberOfLayer1Txs.uncheckedInc();
                          }
                      } else if (logSender == L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) {
                          // Make sure that the system context log wasn't processed yet, to
                          // avoid accident double reading `blockTimestamp` and `previousBlockHash`
                          require(!isSystemContextLogProcessed, "fx");
                          (packedBatchAndL2BlockTimestamp, ) = UnsafeBytes.readUint256(emittedL2Logs, i + 24);
                          (previousBlockHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56);
                          // Mark system context log as processed
                          isSystemContextLogProcessed = true;
                      } else if (logSender == L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR) {
                          (bytes32 bytecodeHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24);
                          require(bytecodeHash == L2ContractHelper.hashL2Bytecode(factoryDeps[currentBytecode]), "k3");
                          currentBytecode = currentBytecode.uncheckedInc();
                      } else {
                          // Only some system contracts could send raw logs from L2 to L1, double check that invariant holds here.
                          revert("ne");
                      }
                  }
                  // To check that only relevant preimages have been included in the calldata
                  require(currentBytecode == factoryDeps.length, "ym");
                  require(currentMessage == _newBlock.l2ArbitraryLengthMessages.length, "pl");
                  // `blockTimestamp` and `previousBlockHash` wasn't read from L2 logs
                  require(isSystemContextLogProcessed, "by");
                  // Making sure that the system contract upgrade was included if needed
                  require(_expectedSystemContractUpgradeTxHash == bytes32(0), "bw");
              }
              /// @notice Commit block
              /// @notice 1. Checks timestamp.
              /// @notice 2. Process L2 logs.
              /// @notice 3. Store block commitments.
              function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
                  external
                  override
                  nonReentrant
                  onlyValidator
              {
                  // Check that we commit blocks after last committed block
                  require(s.storedBlockHashes[s.totalBlocksCommitted] == _hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data
                  require(_newBlocksData.length > 0, "No blocks to commit");
                  bytes32 systemContractsUpgradeTxHash = s.l2SystemContractsUpgradeTxHash;
                  // Upgrades are rarely done so we optimize a case with no active system contracts upgrade.
                  if (systemContractsUpgradeTxHash == bytes32(0) || s.l2SystemContractsUpgradeBlockNumber != 0) {
                      _commitBlocksWithoutSystemContractsUpgrade(_lastCommittedBlockData, _newBlocksData);
                  } else {
                      _commitBlocksWithSystemContractsUpgrade(
                          _lastCommittedBlockData,
                          _newBlocksData,
                          systemContractsUpgradeTxHash
                      );
                  }
                  s.totalBlocksCommitted = s.totalBlocksCommitted + _newBlocksData.length;
              }
              /// @dev Commits new blocks without any system contracts upgrade.
              /// @param _lastCommittedBlockData The data of the last committed block.
              /// @param _newBlocksData An array of block data that needs to be committed.
              function _commitBlocksWithoutSystemContractsUpgrade(
                  StoredBlockInfo memory _lastCommittedBlockData,
                  CommitBlockInfo[] calldata _newBlocksData
              ) internal {
                  for (uint256 i = 0; i < _newBlocksData.length; i = i.uncheckedInc()) {
                      _lastCommittedBlockData = _commitOneBlock(_lastCommittedBlockData, _newBlocksData[i], bytes32(0));
                      s.storedBlockHashes[_lastCommittedBlockData.blockNumber] = _hashStoredBlockInfo(_lastCommittedBlockData);
                      emit BlockCommit(
                          _lastCommittedBlockData.blockNumber,
                          _lastCommittedBlockData.blockHash,
                          _lastCommittedBlockData.commitment
                      );
                  }
              }
              /// @dev Commits new blocks with a system contracts upgrade transaction.
              /// @param _lastCommittedBlockData The data of the last committed block.
              /// @param _newBlocksData An array of block data that needs to be committed.
              /// @param _systemContractUpgradeTxHash The transaction hash of the system contract upgrade.
              function _commitBlocksWithSystemContractsUpgrade(
                  StoredBlockInfo memory _lastCommittedBlockData,
                  CommitBlockInfo[] calldata _newBlocksData,
                  bytes32 _systemContractUpgradeTxHash
              ) internal {
                  // The system contract upgrade is designed to be executed atomically with the new bootloader, a default account,
                  // ZKP verifier, and other system parameters. Hence, we ensure that the upgrade transaction is
                  // carried out within the first block committed after the upgrade.
                  // While the logic of the contract ensures that the s.l2SystemContractsUpgradeBlockNumber is 0 when this function is called,
                  // this check is added just in case. Since it is a hot read, it does not encure noticable gas cost.
                  require(s.l2SystemContractsUpgradeBlockNumber == 0, "ik");
                  // Save the block number where the upgrade transaction was executed.
                  s.l2SystemContractsUpgradeBlockNumber = _newBlocksData[0].blockNumber;
                  for (uint256 i = 0; i < _newBlocksData.length; i = i.uncheckedInc()) {
                      // The upgrade transaction must only be included in the first block.
                      bytes32 expectedUpgradeTxHash = i == 0 ? _systemContractUpgradeTxHash : bytes32(0);
                      _lastCommittedBlockData = _commitOneBlock(
                          _lastCommittedBlockData,
                          _newBlocksData[i],
                          expectedUpgradeTxHash
                      );
                      s.storedBlockHashes[_lastCommittedBlockData.blockNumber] = _hashStoredBlockInfo(_lastCommittedBlockData);
                      emit BlockCommit(
                          _lastCommittedBlockData.blockNumber,
                          _lastCommittedBlockData.blockHash,
                          _lastCommittedBlockData.commitment
                      );
                  }
              }
              /// @dev Pops the priority operations from the priority queue and returns a rolling hash of operations
              function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps) internal returns (bytes32 concatHash) {
                  concatHash = EMPTY_STRING_KECCAK;
                  for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) {
                      PriorityOperation memory priorityOp = s.priorityQueue.popFront();
                      concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash));
                  }
              }
              /// @dev Executes one block
              /// @dev 1. Processes all pending operations (Complete priority requests)
              /// @dev 2. Finalizes block on Ethereum
              /// @dev _executedBlockIdx is an index in the array of the blocks that we want to execute together
              function _executeOneBlock(StoredBlockInfo memory _storedBlock, uint256 _executedBlockIdx) internal {
                  uint256 currentBlockNumber = _storedBlock.blockNumber;
                  require(currentBlockNumber == s.totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order
                  require(
                      _hashStoredBlockInfo(_storedBlock) == s.storedBlockHashes[currentBlockNumber],
                      "exe10" // executing block should be committed
                  );
                  bytes32 priorityOperationsHash = _collectOperationsFromPriorityQueue(_storedBlock.numberOfLayer1Txs);
                  require(priorityOperationsHash == _storedBlock.priorityOperationsHash, "x"); // priority operations hash does not match to expected
                  // Save root hash of L2 -> L1 logs tree
                  s.l2LogsRootHashes[currentBlockNumber] = _storedBlock.l2LogsTreeRoot;
              }
              /// @notice Execute blocks, complete priority operations and process withdrawals.
              /// @notice 1. Processes all pending operations (Complete priority requests)
              /// @notice 2. Finalizes block on Ethereum
              function executeBlocks(StoredBlockInfo[] calldata _blocksData) external nonReentrant onlyValidator {
                  uint256 nBlocks = _blocksData.length;
                  for (uint256 i = 0; i < nBlocks; i = i.uncheckedInc()) {
                      _executeOneBlock(_blocksData[i], i);
                      emit BlockExecution(_blocksData[i].blockNumber, _blocksData[i].blockHash, _blocksData[i].commitment);
                  }
                  uint256 newTotalBlocksExecuted = s.totalBlocksExecuted + nBlocks;
                  s.totalBlocksExecuted = newTotalBlocksExecuted;
                  require(newTotalBlocksExecuted <= s.totalBlocksVerified, "n"); // Can't execute blocks more than committed and proven currently.
                  uint256 blockWhenUpgradeHappened = s.l2SystemContractsUpgradeBlockNumber;
                  if (blockWhenUpgradeHappened != 0 && blockWhenUpgradeHappened <= newTotalBlocksExecuted) {
                      delete s.l2SystemContractsUpgradeTxHash;
                      delete s.l2SystemContractsUpgradeBlockNumber;
                  }
              }
              /// @notice Blocks commitment verification.
              /// @notice Only verifies block commitments without any other processing
              function proveBlocks(
                  StoredBlockInfo calldata _prevBlock,
                  StoredBlockInfo[] calldata _committedBlocks,
                  ProofInput calldata _proof
              ) external nonReentrant onlyValidator {
                  // Save the variables into the stack to save gas on reading them later
                  uint256 currentTotalBlocksVerified = s.totalBlocksVerified;
                  uint256 committedBlocksLength = _committedBlocks.length;
                  // Save the variable from the storage to memory to save gas
                  VerifierParams memory verifierParams = s.verifierParams;
                  // Initialize the array, that will be used as public input to the ZKP
                  uint256[] memory proofPublicInput = new uint256[](committedBlocksLength);
                  // Check that the block passed by the validator is indeed the first unverified block
                  require(_hashStoredBlockInfo(_prevBlock) == s.storedBlockHashes[currentTotalBlocksVerified], "t1");
                  bytes32 prevBlockCommitment = _prevBlock.commitment;
                  for (uint256 i = 0; i < committedBlocksLength; i = i.uncheckedInc()) {
                      currentTotalBlocksVerified = currentTotalBlocksVerified.uncheckedInc();
                      require(_hashStoredBlockInfo(_committedBlocks[i]) == s.storedBlockHashes[currentTotalBlocksVerified], "o1");
                      bytes32 currentBlockCommitment = _committedBlocks[i].commitment;
                      proofPublicInput[i] = _getBlockProofPublicInput(
                          prevBlockCommitment,
                          currentBlockCommitment,
                          _proof,
                          verifierParams
                      );
                      prevBlockCommitment = currentBlockCommitment;
                  }
                  require(currentTotalBlocksVerified <= s.totalBlocksCommitted, "q");
                  bool successVerifyProof = s.verifier.verify_serialized_proof(proofPublicInput, _proof.serializedProof);
                  require(successVerifyProof, "p"); // Proof verification fail
                  // Verify the recursive part that was given to us through the public input
                  bool successProofAggregation = _verifyRecursivePartOfProof(_proof.recursiveAggregationInput);
                  require(successProofAggregation, "hh"); // Proof aggregation must be valid
                  emit BlocksVerification(s.totalBlocksVerified, currentTotalBlocksVerified);
                  s.totalBlocksVerified = currentTotalBlocksVerified;
              }
              /// @dev Gets zk proof public input
              function _getBlockProofPublicInput(
                  bytes32 _prevBlockCommitment,
                  bytes32 _currentBlockCommitment,
                  ProofInput calldata _proof,
                  VerifierParams memory _verifierParams
              ) internal pure returns (uint256) {
                  return
                      uint256(
                          keccak256(
                              abi.encodePacked(
                                  _prevBlockCommitment,
                                  _currentBlockCommitment,
                                  _verifierParams.recursionNodeLevelVkHash,
                                  _verifierParams.recursionLeafLevelVkHash,
                                  _verifierParams.recursionCircuitsSetVksHash,
                                  _proof.recursiveAggregationInput
                              )
                          )
                      ) & INPUT_MASK;
              }
              /// @dev Verify a part of the zkp, that is responsible for the aggregation
              function _verifyRecursivePartOfProof(uint256[] calldata _recursiveAggregationInput) internal view returns (bool) {
                  require(_recursiveAggregationInput.length == 4, "vr");
                  PairingsBn254.G1Point memory pairWithGen = PairingsBn254.new_g1_checked(
                      _recursiveAggregationInput[0],
                      _recursiveAggregationInput[1]
                  );
                  PairingsBn254.G1Point memory pairWithX = PairingsBn254.new_g1_checked(
                      _recursiveAggregationInput[2],
                      _recursiveAggregationInput[3]
                  );
                  PairingsBn254.G2Point memory g2Gen = PairingsBn254.new_g2(
                      [
                          0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                          0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                      ],
                      [
                          0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                          0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                      ]
                  );
                  PairingsBn254.G2Point memory g2X = PairingsBn254.new_g2(
                      [
                          0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                          0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
                      ],
                      [
                          0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                          0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
                      ]
                  );
                  return PairingsBn254.pairingProd2(pairWithGen, g2Gen, pairWithX, g2X);
              }
              /// @notice Reverts unexecuted blocks
              /// @param _newLastBlock block number after which blocks should be reverted
              /// NOTE: Doesn't delete the stored data about blocks, but only decreases
              /// counters that are responsible for the number of blocks
              function revertBlocks(uint256 _newLastBlock) external nonReentrant onlyValidator {
                  require(s.totalBlocksCommitted > _newLastBlock, "v1"); // The last committed block is less than new last block
                  uint256 newTotalBlocksCommitted = _maxU256(_newLastBlock, s.totalBlocksExecuted);
                  if (newTotalBlocksCommitted < s.totalBlocksVerified) {
                      s.totalBlocksVerified = newTotalBlocksCommitted;
                  }
                  s.totalBlocksCommitted = newTotalBlocksCommitted;
                  // Reset the block number of the executed system contracts upgrade transaction if the block
                  // where the system contracts upgrade was committed is among the reverted blocks.
                  if (s.l2SystemContractsUpgradeBlockNumber > newTotalBlocksCommitted) {
                      delete s.l2SystemContractsUpgradeBlockNumber;
                  }
                  emit BlocksRevert(s.totalBlocksCommitted, s.totalBlocksVerified, s.totalBlocksExecuted);
              }
              /// @notice Returns larger of two values
              function _maxU256(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? b : a;
              }
              /// @dev Creates block commitment from its data
              function _createBlockCommitment(CommitBlockInfo calldata _newBlockData) internal view returns (bytes32) {
                  bytes32 passThroughDataHash = keccak256(_blockPassThroughData(_newBlockData));
                  bytes32 metadataHash = keccak256(_blockMetaParameters());
                  bytes32 auxiliaryOutputHash = keccak256(_blockAuxiliaryOutput(_newBlockData));
                  return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash));
              }
              function _blockPassThroughData(CommitBlockInfo calldata _block) internal pure returns (bytes memory) {
                  return
                      abi.encodePacked(
                          _block.indexRepeatedStorageChanges,
                          _block.newStateRoot,
                          uint64(0), // index repeated storage changes in zkPorter
                          bytes32(0) // zkPorter block hash
                      );
              }
              function _blockMetaParameters() internal view returns (bytes memory) {
                  return abi.encodePacked(s.zkPorterIsAvailable, s.l2BootloaderBytecodeHash, s.l2DefaultAccountBytecodeHash);
              }
              function _blockAuxiliaryOutput(CommitBlockInfo calldata _block) internal pure returns (bytes memory) {
                  require(_block.initialStorageChanges.length <= MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, "pf");
                  require(_block.repeatedStorageChanges.length <= MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, "py");
                  require(_block.l2Logs.length <= MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, "pu");
                  bytes32 initialStorageChangesHash = keccak256(_block.initialStorageChanges);
                  bytes32 repeatedStorageChangesHash = keccak256(_block.repeatedStorageChanges);
                  bytes32 l2ToL1LogsHash = keccak256(_block.l2Logs);
                  return abi.encode(_block.l2LogsTreeRoot, l2ToL1LogsHash, initialStorageChangesHash, repeatedStorageChangesHash);
              }
              /// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo
              function _hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) {
                  return keccak256(abi.encode(_storedBlockInfo));
              }
          }
          pragma solidity ^0.8.0;
          // SPDX-License-Identifier: UNLICENSED
          interface IBase {
              function getName() external view returns (string memory);
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./IBase.sol";
          interface IExecutor is IBase {
              /// @notice Rollup block stored data
              /// @param blockNumber Rollup block number
              /// @param blockHash Hash of L2 block
              /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
              /// @param numberOfLayer1Txs Number of priority operations to be processed
              /// @param priorityOperationsHash Hash of all priority operations from this block
              /// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block
              /// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant
              /// @param commitment Verified input for the zkSync circuit
              struct StoredBlockInfo {
                  uint64 blockNumber;
                  bytes32 blockHash;
                  uint64 indexRepeatedStorageChanges;
                  uint256 numberOfLayer1Txs;
                  bytes32 priorityOperationsHash;
                  bytes32 l2LogsTreeRoot;
                  uint256 timestamp;
                  bytes32 commitment;
              }
              /// @notice Data needed to commit new block
              /// @param blockNumber Number of the committed block
              /// @param timestamp Unix timestamp denoting the start of the block execution
              /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
              /// @param newStateRoot The state root of the full state tree
              /// @param numberOfLayer1Txs Number of priority operations to be processed
              /// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block
              /// @param priorityOperationsHash Hash of all priority operations from this block
              /// @param initialStorageChanges Storage write access as a concatenation key-value
              /// @param repeatedStorageChanges Storage write access as a concatenation index-value
              /// @param l2Logs concatenation of all L2 -> L1 logs in the block
              /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract
              /// @param factoryDeps array of l2 bytecodes that were marked as known on L2
              struct CommitBlockInfo {
                  uint64 blockNumber;
                  uint64 timestamp;
                  uint64 indexRepeatedStorageChanges;
                  bytes32 newStateRoot;
                  uint256 numberOfLayer1Txs;
                  bytes32 l2LogsTreeRoot;
                  bytes32 priorityOperationsHash;
                  bytes initialStorageChanges;
                  bytes repeatedStorageChanges;
                  bytes l2Logs;
                  bytes[] l2ArbitraryLengthMessages;
                  bytes[] factoryDeps;
              }
              /// @notice Recursive proof input data (individual commitments are constructed onchain)
              struct ProofInput {
                  uint256[] recursiveAggregationInput;
                  uint256[] serializedProof;
              }
              function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
                  external;
              function proveBlocks(
                  StoredBlockInfo calldata _prevBlock,
                  StoredBlockInfo[] calldata _committedBlocks,
                  ProofInput calldata _proof
              ) external;
              function executeBlocks(StoredBlockInfo[] calldata _blocksData) external;
              function revertBlocks(uint256 _newLastBlock) external;
              /// @notice Event emitted when a block is committed
              event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
              /// @notice Event emitted when blocks are verified
              event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock);
              /// @notice Event emitted when a block is executed
              event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);
              /// @notice Event emitted when blocks are reverted
              event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          library PairingsBn254 {
              uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
              uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
              uint256 constant bn254_b_coeff = 3;
              struct G1Point {
                  uint256 X;
                  uint256 Y;
              }
              struct Fr {
                  uint256 value;
              }
              function new_fr(uint256 fr) internal pure returns (Fr memory) {
                  require(fr < r_mod);
                  return Fr({value: fr});
              }
              function copy(Fr memory self) internal pure returns (Fr memory n) {
                  n.value = self.value;
              }
              function assign(Fr memory self, Fr memory other) internal pure {
                  self.value = other.value;
              }
              function inverse(Fr memory fr) internal view returns (Fr memory) {
                  require(fr.value != 0);
                  return pow(fr, r_mod - 2);
              }
              function add_assign(Fr memory self, Fr memory other) internal pure {
                  self.value = addmod(self.value, other.value, r_mod);
              }
              function sub_assign(Fr memory self, Fr memory other) internal pure {
                  self.value = addmod(self.value, r_mod - other.value, r_mod);
              }
              function mul_assign(Fr memory self, Fr memory other) internal pure {
                  self.value = mulmod(self.value, other.value, r_mod);
              }
              function pow(Fr memory self, uint256 power) internal view returns (Fr memory) {
                  uint256[6] memory input = [32, 32, 32, self.value, power, r_mod];
                  uint256[1] memory result;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20)
                  }
                  require(success);
                  return Fr({value: result[0]});
              }
              // Encoding of field elements is: X[0] * z + X[1]
              struct G2Point {
                  uint256[2] X;
                  uint256[2] Y;
              }
              function P1() internal pure returns (G1Point memory) {
                  return G1Point(1, 2);
              }
              function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) {
                  return G1Point(x, y);
              }
              // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
              function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
                  if (x == 0 && y == 0) {
                      // point of infinity is (0,0)
                      return G1Point(x, y);
                  }
                  // check encoding
                  require(x < q_mod, "x axis isn't valid");
                  require(y < q_mod, "y axis isn't valid");
                  // check on curve
                  uint256 lhs = mulmod(y, y, q_mod); // y^2
                  uint256 rhs = mulmod(x, x, q_mod); // x^2
                  rhs = mulmod(rhs, x, q_mod); // x^3
                  rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b
                  require(lhs == rhs, "is not on curve");
                  return G1Point(x, y);
              }
              function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) {
                  return G2Point(x, y);
              }
              function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) {
                  result.X = self.X;
                  result.Y = self.Y;
              }
              function P2() internal pure returns (G2Point memory) {
                  // for some reason ethereum expects to have c1*v + c0 form
                  return
                      G2Point(
                          [
                              0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                              0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                          ],
                          [
                              0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                              0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                          ]
                      );
              }
              function negate(G1Point memory self) internal pure {
                  // The prime q in the base field F_q for G1
                  if (self.Y == 0) {
                      require(self.X == 0);
                      return;
                  }
                  self.Y = q_mod - self.Y;
              }
              function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
                  point_add_into_dest(p1, p2, r);
                  return r;
              }
              function point_add_assign(G1Point memory p1, G1Point memory p2) internal view {
                  point_add_into_dest(p1, p2, p1);
              }
              function point_add_into_dest(
                  G1Point memory p1,
                  G1Point memory p2,
                  G1Point memory dest
              ) internal view {
                  if (p2.X == 0 && p2.Y == 0) {
                      // we add zero, nothing happens
                      dest.X = p1.X;
                      dest.Y = p1.Y;
                      return;
                  } else if (p1.X == 0 && p1.Y == 0) {
                      // we add into zero, and we add non-zero point
                      dest.X = p2.X;
                      dest.Y = p2.Y;
                      return;
                  } else {
                      uint256[4] memory input;
                      input[0] = p1.X;
                      input[1] = p1.Y;
                      input[2] = p2.X;
                      input[3] = p2.Y;
                      bool success;
                      assembly {
                          success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
                      }
                      require(success);
                  }
              }
              function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view {
                  point_sub_into_dest(p1, p2, p1);
              }
              function point_sub_into_dest(
                  G1Point memory p1,
                  G1Point memory p2,
                  G1Point memory dest
              ) internal view {
                  if (p2.X == 0 && p2.Y == 0) {
                      // we subtracted zero, nothing happens
                      dest.X = p1.X;
                      dest.Y = p1.Y;
                      return;
                  } else if (p1.X == 0 && p1.Y == 0) {
                      // we subtract from zero, and we subtract non-zero point
                      dest.X = p2.X;
                      dest.Y = q_mod - p2.Y;
                      return;
                  } else {
                      uint256[4] memory input;
                      input[0] = p1.X;
                      input[1] = p1.Y;
                      input[2] = p2.X;
                      input[3] = q_mod - p2.Y;
                      bool success = false;
                      assembly {
                          success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
                      }
                      require(success);
                  }
              }
              function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) {
                  // https://eips.ethereum.org/EIPS/eip-197
                  // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0)
                  if (p.X == 0 && p.Y == 1) {
                      p.Y = 0;
                  }
                  point_mul_into_dest(p, s, r);
                  return r;
              }
              function point_mul_assign(G1Point memory p, Fr memory s) internal view {
                  point_mul_into_dest(p, s, p);
              }
              function point_mul_into_dest(
                  G1Point memory p,
                  Fr memory s,
                  G1Point memory dest
              ) internal view {
                  uint256[3] memory input;
                  input[0] = p.X;
                  input[1] = p.Y;
                  input[2] = s.value;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 7, input, 0x60, dest, 0x40)
                  }
                  require(success);
              }
              function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
                  require(p1.length == p2.length);
                  uint256 elements = p1.length;
                  uint256 inputSize = elements * 6;
                  uint256[] memory input = new uint256[](inputSize);
                  for (uint256 i = 0; i < elements; ) {
                      input[i * 6 + 0] = p1[i].X;
                      input[i * 6 + 1] = p1[i].Y;
                      input[i * 6 + 2] = p2[i].X[0];
                      input[i * 6 + 3] = p2[i].X[1];
                      input[i * 6 + 4] = p2[i].Y[0];
                      input[i * 6 + 5] = p2[i].Y[1];
                      unchecked {
                          ++i;
                      }
                  }
                  uint256[1] memory out;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
                  }
                  require(success);
                  return out[0] != 0;
              }
              /// Convenience method for a pairing check for two pairs.
              function pairingProd2(
                  G1Point memory a1,
                  G2Point memory a2,
                  G1Point memory b1,
                  G2Point memory b2
              ) internal view returns (bool) {
                  G1Point[] memory p1 = new G1Point[](2);
                  G2Point[] memory p2 = new G2Point[](2);
                  p1[0] = a1;
                  p1[1] = b1;
                  p2[0] = a2;
                  p2[1] = b2;
                  return pairing(p1, p2);
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          /// @notice The structure that contains meta information of the L2 transaction that was requested from L1
          /// @dev The weird size of fields was selected specifically to minimize the structure storage size
          /// @param canonicalTxHash Hashed L2 transaction data that is needed to process it
          /// @param expirationTimestamp Expiration timestamp for this request (must be satisfied before)
          /// @param layer2Tip Additional payment to the validator as an incentive to perform the operation
          struct PriorityOperation {
              bytes32 canonicalTxHash;
              uint64 expirationTimestamp;
              uint192 layer2Tip;
          }
          /// @author Matter Labs
          /// @dev The library provides the API to interact with the priority queue container
          /// @dev Order of processing operations from queue - FIFO (Fist in - first out)
          library PriorityQueue {
              using PriorityQueue for Queue;
              /// @notice Container that stores priority operations
              /// @param data The inner mapping that saves priority operation by its index
              /// @param head The pointer to the first unprocessed priority operation, equal to the tail if the queue is empty
              /// @param tail The pointer to the free slot
              struct Queue {
                  mapping(uint256 => PriorityOperation) data;
                  uint256 tail;
                  uint256 head;
              }
              /// @notice Returns zero if and only if no operations were processed from the queue
              /// @return Index of the oldest priority operation that wasn't processed yet
              function getFirstUnprocessedPriorityTx(Queue storage _queue) internal view returns (uint256) {
                  return _queue.head;
              }
              /// @return The total number of priority operations that were added to the priority queue, including all processed ones
              function getTotalPriorityTxs(Queue storage _queue) internal view returns (uint256) {
                  return _queue.tail;
              }
              /// @return The total number of unprocessed priority operations in a priority queue
              function getSize(Queue storage _queue) internal view returns (uint256) {
                  return uint256(_queue.tail - _queue.head);
              }
              /// @return Whether the priority queue contains no operations
              function isEmpty(Queue storage _queue) internal view returns (bool) {
                  return _queue.tail == _queue.head;
              }
              /// @notice Add the priority operation to the end of the priority queue
              function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal {
                  // Save value into the stack to avoid double reading from the storage
                  uint256 tail = _queue.tail;
                  _queue.data[tail] = _operation;
                  _queue.tail = tail + 1;
              }
              /// @return The first unprocessed priority operation from the queue
              function front(Queue storage _queue) internal view returns (PriorityOperation memory) {
                  require(!_queue.isEmpty(), "D"); // priority queue is empty
                  return _queue.data[_queue.head];
              }
              /// @notice Remove the first unprocessed priority operation from the queue
              /// @return priorityOperation that was popped from the priority queue
              function popFront(Queue storage _queue) internal returns (PriorityOperation memory priorityOperation) {
                  require(!_queue.isEmpty(), "s"); // priority queue is empty
                  // Save value into the stack to avoid double reading from the storage
                  uint256 head = _queue.head;
                  priorityOperation = _queue.data[head];
                  delete _queue.data[head];
                  _queue.head = head + 1;
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./PairingsBn254.sol";
          library TranscriptLib {
              // flip                    0xe000000000000000000000000000000000000000000000000000000000000000;
              uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
              uint32 constant DST_0 = 0;
              uint32 constant DST_1 = 1;
              uint32 constant DST_CHALLENGE = 2;
              struct Transcript {
                  bytes32 state_0;
                  bytes32 state_1;
                  uint32 challenge_counter;
              }
              function new_transcript() internal pure returns (Transcript memory t) {
                  t.state_0 = bytes32(0);
                  t.state_1 = bytes32(0);
                  t.challenge_counter = 0;
              }
              function update_with_u256(Transcript memory self, uint256 value) internal pure {
                  bytes32 old_state_0 = self.state_0;
                  self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value));
                  self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value));
              }
              function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure {
                  update_with_u256(self, value.value);
              }
              function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure {
                  update_with_u256(self, p.X);
                  update_with_u256(self, p.Y);
              }
              function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) {
                  bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter));
                  self.challenge_counter += 1;
                  challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK});
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./libraries/PairingsBn254.sol";
          import "./libraries/TranscriptLib.sol";
          import "../common/libraries/UncheckedMath.sol";
          uint256 constant STATE_WIDTH = 4;
          uint256 constant NUM_G2_ELS = 2;
          struct VerificationKey {
              uint256 domain_size;
              uint256 num_inputs;
              PairingsBn254.Fr omega;
              PairingsBn254.G1Point[2] gate_selectors_commitments;
              PairingsBn254.G1Point[8] gate_setup_commitments;
              PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments;
              PairingsBn254.G1Point lookup_selector_commitment;
              PairingsBn254.G1Point[4] lookup_tables_commitments;
              PairingsBn254.G1Point lookup_table_type_commitment;
              PairingsBn254.Fr[STATE_WIDTH - 1] non_residues;
              PairingsBn254.G2Point[NUM_G2_ELS] g2_elements;
          }
          contract Plonk4VerifierWithAccessToDNext {
              using PairingsBn254 for PairingsBn254.G1Point;
              using PairingsBn254 for PairingsBn254.G2Point;
              using PairingsBn254 for PairingsBn254.Fr;
              using TranscriptLib for TranscriptLib.Transcript;
              using UncheckedMath for uint256;
              struct Proof {
                  uint256[] input_values;
                  // commitments
                  PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments;
                  PairingsBn254.G1Point copy_permutation_grand_product_commitment;
                  PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments;
                  // openings
                  PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z;
                  PairingsBn254.Fr[1] state_polys_openings_at_z_omega;
                  PairingsBn254.Fr[1] gate_selectors_openings_at_z;
                  PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z;
                  PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega;
                  PairingsBn254.Fr quotient_poly_opening_at_z;
                  PairingsBn254.Fr linearization_poly_opening_at_z;
                  // lookup commitments
                  PairingsBn254.G1Point lookup_s_poly_commitment;
                  PairingsBn254.G1Point lookup_grand_product_commitment;
                  // lookup openings
                  PairingsBn254.Fr lookup_s_poly_opening_at_z_omega;
                  PairingsBn254.Fr lookup_grand_product_opening_at_z_omega;
                  PairingsBn254.Fr lookup_t_poly_opening_at_z;
                  PairingsBn254.Fr lookup_t_poly_opening_at_z_omega;
                  PairingsBn254.Fr lookup_selector_poly_opening_at_z;
                  PairingsBn254.Fr lookup_table_type_poly_opening_at_z;
                  PairingsBn254.G1Point opening_proof_at_z;
                  PairingsBn254.G1Point opening_proof_at_z_omega;
              }
              struct PartialVerifierState {
                  PairingsBn254.Fr zero;
                  PairingsBn254.Fr alpha;
                  PairingsBn254.Fr beta;
                  PairingsBn254.Fr gamma;
                  PairingsBn254.Fr[9] alpha_values;
                  PairingsBn254.Fr eta;
                  PairingsBn254.Fr beta_lookup;
                  PairingsBn254.Fr gamma_lookup;
                  PairingsBn254.Fr beta_plus_one;
                  PairingsBn254.Fr beta_gamma;
                  PairingsBn254.Fr v;
                  PairingsBn254.Fr u;
                  PairingsBn254.Fr z;
                  PairingsBn254.Fr z_omega;
                  PairingsBn254.Fr z_minus_last_omega;
                  PairingsBn254.Fr l_0_at_z;
                  PairingsBn254.Fr l_n_minus_one_at_z;
                  PairingsBn254.Fr t;
                  PairingsBn254.G1Point tp;
              }
              function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at)
                  internal
                  view
                  returns (PairingsBn254.Fr memory num)
              {
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size);
                  PairingsBn254.Fr memory den = at.copy();
                  den.sub_assign(one);
                  den.mul_assign(size_fe);
                  den = den.inverse();
                  num = at.pow(domain_size);
                  num.sub_assign(one);
                  num.mul_assign(den);
              }
              function evaluate_lagrange_poly_out_of_domain(
                  uint256 poly_num,
                  uint256 domain_size,
                  PairingsBn254.Fr memory omega,
                  PairingsBn254.Fr memory at
              ) internal view returns (PairingsBn254.Fr memory res) {
                  // (omega^i / N) / (X - omega^i) * (X^N - 1)
                  require(poly_num < domain_size);
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
                  res = at.pow(domain_size);
                  res.sub_assign(one);
                  require(res.value != 0); // Vanishing polynomial cannot be zero at point `at`
                  res.mul_assign(omega_power);
                  PairingsBn254.Fr memory den = PairingsBn254.copy(at);
                  den.sub_assign(omega_power);
                  den.mul_assign(PairingsBn254.new_fr(domain_size));
                  den = den.inverse();
                  res.mul_assign(den);
              }
              function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at)
                  internal
                  view
                  returns (PairingsBn254.Fr memory res)
              {
                  res = at.pow(domain_size);
                  res.sub_assign(PairingsBn254.new_fr(1));
              }
              function initialize_transcript(Proof memory proof, VerificationKey memory vk)
                  internal
                  pure
                  returns (PartialVerifierState memory state)
              {
                  TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript();
                  for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
                      transcript.update_with_u256(proof.input_values[i]);
                  }
                  for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                      transcript.update_with_g1(proof.state_polys_commitments[i]);
                  }
                  state.eta = transcript.get_challenge();
                  transcript.update_with_g1(proof.lookup_s_poly_commitment);
                  state.beta = transcript.get_challenge();
                  state.gamma = transcript.get_challenge();
                  transcript.update_with_g1(proof.copy_permutation_grand_product_commitment);
                  state.beta_lookup = transcript.get_challenge();
                  state.gamma_lookup = transcript.get_challenge();
                  transcript.update_with_g1(proof.lookup_grand_product_commitment);
                  state.alpha = transcript.get_challenge();
                  for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                      transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]);
                  }
                  state.z = transcript.get_challenge();
                  transcript.update_with_fr(proof.quotient_poly_opening_at_z);
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.state_polys_openings_at_z[i]);
                  }
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]);
                  }
                  for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]);
                  }
                  for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]);
                  }
                  state.z_omega = state.z.copy();
                  state.z_omega.mul_assign(vk.omega);
                  transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega);
                  transcript.update_with_fr(proof.lookup_t_poly_opening_at_z);
                  transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z);
                  transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z);
                  transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega);
                  transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega);
                  transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega);
                  transcript.update_with_fr(proof.linearization_poly_opening_at_z);
                  state.v = transcript.get_challenge();
                  transcript.update_with_g1(proof.opening_proof_at_z);
                  transcript.update_with_g1(proof.opening_proof_at_z_omega);
                  state.u = transcript.get_challenge();
              }
              // compute some powers of challenge alpha([alpha^1, .. alpha^8])
              function compute_powers_of_alpha(PartialVerifierState memory state) public pure {
                  require(state.alpha.value != 0);
                  state.alpha_values[0] = PairingsBn254.new_fr(1);
                  state.alpha_values[1] = state.alpha.copy();
                  PairingsBn254.Fr memory current_alpha = state.alpha.copy();
                  for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) {
                      current_alpha.mul_assign(state.alpha);
                      state.alpha_values[i] = current_alpha.copy();
                  }
              }
              function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
                  // we initialize all challenges beforehand, we can draw each challenge in its own place
                  PartialVerifierState memory state = initialize_transcript(proof, vk);
                  if (verify_quotient_evaluation(vk, proof, state) == false) {
                      return false;
                  }
                  require(proof.state_polys_openings_at_z_omega.length == 1);
                  PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1();
                  {
                      // block scope
                      PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
                      PairingsBn254.Fr memory current_z = z_in_domain_size.copy();
                      PairingsBn254.G1Point memory tp;
                      // start from i =1
                      for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                          tp = proof.quotient_poly_parts_commitments[i].copy_g1();
                          tp.point_mul_assign(current_z);
                          quotient_result.point_add_assign(tp);
                          current_z.mul_assign(z_in_domain_size);
                      }
                  }
                  Queries memory queries = prepare_queries(vk, proof, state);
                  queries.commitments_at_z[0] = quotient_result;
                  queries.values_at_z[0] = proof.quotient_poly_opening_at_z;
                  queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state);
                  queries.values_at_z[1] = proof.linearization_poly_opening_at_z;
                  require(queries.commitments_at_z.length == queries.values_at_z.length);
                  PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0];
                  PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0];
                  PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
                  PairingsBn254.G1Point memory scaled;
                  for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) {
                      aggregation_challenge.mul_assign(state.v);
                      scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge);
                      aggregated_commitment_at_z.point_add_assign(scaled);
                      state.t = queries.values_at_z[i];
                      state.t.mul_assign(aggregation_challenge);
                      aggregated_opening_at_z.add_assign(state.t);
                  }
                  aggregation_challenge.mul_assign(state.v);
                  PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul(
                      aggregation_challenge
                  );
                  PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0];
                  aggregated_opening_at_z_omega.mul_assign(aggregation_challenge);
                  for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) {
                      aggregation_challenge.mul_assign(state.v);
                      scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge);
                      aggregated_commitment_at_z_omega.point_add_assign(scaled);
                      state.t = queries.values_at_z_omega[i];
                      state.t.mul_assign(aggregation_challenge);
                      aggregated_opening_at_z_omega.add_assign(state.t);
                  }
                  return
                      final_pairing(
                          vk.g2_elements,
                          proof,
                          state,
                          aggregated_commitment_at_z,
                          aggregated_commitment_at_z_omega,
                          aggregated_opening_at_z,
                          aggregated_opening_at_z_omega
                      );
              }
              function verify_quotient_evaluation(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) internal view returns (bool) {
                  uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
                  for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) {
                      lagrange_poly_numbers[i] = i;
                  }
                  require(vk.num_inputs > 0);
                  PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0);
                  for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
                      state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z);
                      state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
                      inputs_term.add_assign(state.t);
                  }
                  inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]);
                  PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy();
                  result.add_assign(inputs_term);
                  // compute powers of alpha
                  compute_powers_of_alpha(state);
                  PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
                  factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
                  // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega)
                  require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1);
                  PairingsBn254.Fr memory t; // TMP;
                  for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      t = proof.copy_permutation_polys_openings_at_z[i].copy();
                      t.mul_assign(state.beta);
                      t.add_assign(proof.state_polys_openings_at_z[i]);
                      t.add_assign(state.gamma);
                      factor.mul_assign(t);
                  }
                  t = proof.state_polys_openings_at_z[3].copy();
                  t.add_assign(state.gamma);
                  factor.mul_assign(t);
                  result.sub_assign(factor);
                  // - L_0(z) * alpha_1
                  PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z);
                  l_0_at_z.mul_assign(state.alpha_values[4 + 1]);
                  result.sub_assign(l_0_at_z);
                  PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state);
                  result.add_assign(lookup_quotient_contrib);
                  PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy();
                  lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z));
                  return lhs.value == result.value;
              }
              function lookup_quotient_contribution(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) internal view returns (PairingsBn254.Fr memory result) {
                  PairingsBn254.Fr memory t;
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  state.beta_plus_one = state.beta_lookup.copy();
                  state.beta_plus_one.add_assign(one);
                  state.beta_gamma = state.beta_plus_one.copy();
                  state.beta_gamma.mul_assign(state.gamma_lookup);
                  // (s'*beta + gamma)*(zw')*alpha
                  t = proof.lookup_s_poly_opening_at_z_omega.copy();
                  t.mul_assign(state.beta_lookup);
                  t.add_assign(state.beta_gamma);
                  t.mul_assign(proof.lookup_grand_product_opening_at_z_omega);
                  t.mul_assign(state.alpha_values[6]);
                  // (z - omega^{n-1}) for this part
                  PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1);
                  state.z_minus_last_omega = state.z.copy();
                  state.z_minus_last_omega.sub_assign(last_omega);
                  t.mul_assign(state.z_minus_last_omega);
                  result.add_assign(t);
                  // - alpha_1 * L_{0}(z)
                  state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
                  t = state.l_0_at_z.copy();
                  t.mul_assign(state.alpha_values[6 + 1]);
                  result.sub_assign(t);
                  // - alpha_2 * beta_gamma_powered L_{n-1}(z)
                  PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1);
                  state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain(
                      vk.domain_size - 1,
                      vk.domain_size,
                      vk.omega,
                      state.z
                  );
                  t = state.l_n_minus_one_at_z.copy();
                  t.mul_assign(beta_gamma_powered);
                  t.mul_assign(state.alpha_values[6 + 2]);
                  result.sub_assign(t);
              }
              function aggregated_linearization_commitment(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) internal view returns (PairingsBn254.G1Point memory result) {
                  // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next)
                  result = PairingsBn254.new_g1(0, 0);
                  // Q_a * A
                  PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul(
                      proof.state_polys_openings_at_z[0]
                  );
                  result.point_add_assign(scaled);
                  // Q_b * B
                  scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]);
                  result.point_add_assign(scaled);
                  // Q_c * C
                  scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]);
                  result.point_add_assign(scaled);
                  // Q_d * D
                  scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]);
                  result.point_add_assign(scaled);
                  // Q_m* A*B or Q_ab*A*B
                  PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy();
                  t.mul_assign(proof.state_polys_openings_at_z[1]);
                  scaled = vk.gate_setup_commitments[4].point_mul(t);
                  result.point_add_assign(scaled);
                  // Q_AC* A*C
                  t = proof.state_polys_openings_at_z[0].copy();
                  t.mul_assign(proof.state_polys_openings_at_z[2]);
                  scaled = vk.gate_setup_commitments[5].point_mul(t);
                  result.point_add_assign(scaled);
                  // Q_const
                  result.point_add_assign(vk.gate_setup_commitments[6]);
                  // Q_dNext * D_next
                  scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]);
                  result.point_add_assign(scaled);
                  result.point_mul_assign(proof.gate_selectors_openings_at_z[0]);
                  PairingsBn254.G1Point
                      memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution(
                          vk,
                          proof,
                          state
                      );
                  result.point_add_assign(rescue_custom_gate_linearization_contrib);
                  require(vk.non_residues.length == STATE_WIDTH - 1);
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) {
                      t = state.z.copy();
                      if (i == 0) {
                          t.mul_assign(one);
                      } else {
                          t.mul_assign(vk.non_residues[i - 1]);
                      }
                      t.mul_assign(state.beta);
                      t.add_assign(state.gamma);
                      t.add_assign(proof.state_polys_openings_at_z[i]);
                      factor.mul_assign(t);
                      unchecked {
                          ++i;
                      }
                  }
                  scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
                  // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X)
                  factor = state.alpha_values[4].copy();
                  factor.mul_assign(state.beta);
                  factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
                  for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                      t = proof.copy_permutation_polys_openings_at_z[i].copy();
                      t.mul_assign(state.beta);
                      t.add_assign(state.gamma);
                      t.add_assign(proof.state_polys_openings_at_z[i]);
                      factor.mul_assign(t);
                  }
                  scaled = vk.permutation_commitments[3].point_mul(factor);
                  result.point_sub_assign(scaled);
                  // + L_0(z) * Z(x)
                  state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
                  require(state.l_0_at_z.value != 0);
                  factor = state.l_0_at_z.copy();
                  factor.mul_assign(state.alpha_values[4 + 1]);
                  scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
                  PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state);
                  result.point_add_assign(lookup_linearization_contrib);
              }
              function rescue_custom_gate_linearization_contribution(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) public view returns (PairingsBn254.G1Point memory result) {
                  PairingsBn254.Fr memory t;
                  PairingsBn254.Fr memory intermediate_result;
                  // a^2 - b = 0
                  t = proof.state_polys_openings_at_z[0].copy();
                  t.mul_assign(t);
                  t.sub_assign(proof.state_polys_openings_at_z[1]);
                  // t.mul_assign(challenge1);
                  t.mul_assign(state.alpha_values[1]);
                  intermediate_result.add_assign(t);
                  // b^2 - c = 0
                  t = proof.state_polys_openings_at_z[1].copy();
                  t.mul_assign(t);
                  t.sub_assign(proof.state_polys_openings_at_z[2]);
                  t.mul_assign(state.alpha_values[1 + 1]);
                  intermediate_result.add_assign(t);
                  // c*a - d = 0;
                  t = proof.state_polys_openings_at_z[2].copy();
                  t.mul_assign(proof.state_polys_openings_at_z[0]);
                  t.sub_assign(proof.state_polys_openings_at_z[3]);
                  t.mul_assign(state.alpha_values[1 + 2]);
                  intermediate_result.add_assign(t);
                  result = vk.gate_selectors_commitments[1].point_mul(intermediate_result);
              }
              function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state)
                  internal
                  view
                  returns (PairingsBn254.G1Point memory result)
              {
                  PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0);
                  PairingsBn254.Fr memory t;
                  PairingsBn254.Fr memory factor;
                  // s(x) from the Z(x*omega)*(\\gamma*(1 + \\beta) + s(x) + \\beta * s(x*omega)))
                  factor = proof.lookup_grand_product_opening_at_z_omega.copy();
                  factor.mul_assign(state.alpha_values[6]);
                  factor.mul_assign(state.z_minus_last_omega);
                  PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
                  // Z(x) from - alpha_0 * Z(x) * (\\beta + 1) * (\\gamma + f(x)) * (\\gamma(1 + \\beta) + t(x) + \\beta * t(x*omega))
                  // + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z)
                  // accumulate coefficient
                  factor = proof.lookup_t_poly_opening_at_z_omega.copy();
                  factor.mul_assign(state.beta_lookup);
                  factor.add_assign(proof.lookup_t_poly_opening_at_z);
                  factor.add_assign(state.beta_gamma);
                  // (\\gamma + f(x))
                  PairingsBn254.Fr memory f_reconstructed;
                  PairingsBn254.Fr memory current = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory tmp0;
                  for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                      tmp0 = proof.state_polys_openings_at_z[i].copy();
                      tmp0.mul_assign(current);
                      f_reconstructed.add_assign(tmp0);
                      current.mul_assign(state.eta);
                  }
                  // add type of table
                  t = proof.lookup_table_type_poly_opening_at_z.copy();
                  t.mul_assign(current);
                  f_reconstructed.add_assign(t);
                  f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z);
                  f_reconstructed.add_assign(state.gamma_lookup);
                  // end of (\\gamma + f(x)) part
                  factor.mul_assign(f_reconstructed);
                  factor.mul_assign(state.beta_plus_one);
                  t = zero.copy();
                  t.sub_assign(factor);
                  factor = t;
                  factor.mul_assign(state.alpha_values[6]);
                  // Multiply by (z - omega^{n-1})
                  factor.mul_assign(state.z_minus_last_omega);
                  // L_{0}(z) in front of Z(x)
                  t = state.l_0_at_z.copy();
                  t.mul_assign(state.alpha_values[6 + 1]);
                  factor.add_assign(t);
                  // L_{n-1}(z) in front of Z(x)
                  t = state.l_n_minus_one_at_z.copy();
                  t.mul_assign(state.alpha_values[6 + 2]);
                  factor.add_assign(t);
                  scaled = proof.lookup_grand_product_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
              }
              struct Queries {
                  PairingsBn254.G1Point[13] commitments_at_z;
                  PairingsBn254.Fr[13] values_at_z;
                  PairingsBn254.G1Point[6] commitments_at_z_omega;
                  PairingsBn254.Fr[6] values_at_z_omega;
              }
              function prepare_queries(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) public view returns (Queries memory queries) {
                  // we set first two items in calee side so start idx from 2
                  uint256 idx = 2;
                  for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                      queries.commitments_at_z[idx] = proof.state_polys_commitments[i];
                      queries.values_at_z[idx] = proof.state_polys_openings_at_z[i];
                      idx = idx.uncheckedInc();
                  }
                  require(proof.gate_selectors_openings_at_z.length == 1);
                  queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0];
                  queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0];
                  idx = idx.uncheckedInc();
                  for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                      queries.commitments_at_z[idx] = vk.permutation_commitments[i];
                      queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i];
                      idx = idx.uncheckedInc();
                  }
                  queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment;
                  queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1];
                  queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega;
                  queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0];
                  PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0];
                  PairingsBn254.Fr memory current_eta = state.eta.copy();
                  for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) {
                      state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta);
                      lookup_t_poly_commitment_aggregated.point_add_assign(state.tp);
                      current_eta.mul_assign(state.eta);
                  }
                  queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated;
                  queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z;
                  idx = idx.uncheckedInc();
                  queries.commitments_at_z[idx] = vk.lookup_selector_commitment;
                  queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z;
                  idx = idx.uncheckedInc();
                  queries.commitments_at_z[idx] = vk.lookup_table_type_commitment;
                  queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z;
                  queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment;
                  queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega;
                  queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment;
                  queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega;
                  queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated;
                  queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega;
              }
              function final_pairing(
                  // VerificationKey memory vk,
                  PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements,
                  Proof memory proof,
                  PartialVerifierState memory state,
                  PairingsBn254.G1Point memory aggregated_commitment_at_z,
                  PairingsBn254.G1Point memory aggregated_commitment_at_z_omega,
                  PairingsBn254.Fr memory aggregated_opening_at_z,
                  PairingsBn254.Fr memory aggregated_opening_at_z_omega
              ) internal view returns (bool) {
                  // q(x) = f(x) - f(z) / (x - z)
                  // q(x) * (x-z)  = f(x) - f(z)
                  // f(x)
                  PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1();
                  aggregated_commitment_at_z_omega.point_mul_assign(state.u);
                  pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega);
                  // - f(z)*g
                  PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy();
                  aggregated_value.mul_assign(state.u);
                  aggregated_value.add_assign(aggregated_opening_at_z);
                  PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value);
                  pair_with_generator.point_sub_assign(tp);
                  // +z * q(x)
                  tp = proof.opening_proof_at_z.point_mul(state.z);
                  PairingsBn254.Fr memory t = state.z_omega.copy();
                  t.mul_assign(state.u);
                  PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t);
                  tp.point_add_assign(t1);
                  pair_with_generator.point_add_assign(tp);
                  // rhs
                  PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u);
                  pair_with_x.point_add_assign(proof.opening_proof_at_z);
                  pair_with_x.negate();
                  // Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]`
                  // so we handle it in code generation step
                  PairingsBn254.G2Point memory first_g2 = g2_elements[0];
                  PairingsBn254.G2Point memory second_g2 = g2_elements[1];
                  return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2);
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./Verifier.sol";
          import "../common/interfaces/IAllowList.sol";
          import "./libraries/PriorityQueue.sol";
          /// @notice Indicates whether an upgrade is initiated and if yes what type
          /// @param None Upgrade is NOT initiated
          /// @param Transparent Fully transparent upgrade is initiated, upgrade data is publicly known
          /// @param Shadow Shadow upgrade is initiated, upgrade data is hidden
          enum UpgradeState {
              None,
              Transparent,
              Shadow
          }
          /// @dev Logically separated part of the storage structure, which is responsible for everything related to proxy upgrades and diamond cuts
          /// @param proposedUpgradeHash The hash of the current upgrade proposal, zero if there is no active proposal
          /// @param state Indicates whether an upgrade is initiated and if yes what type
          /// @param securityCouncil Address which has the permission to approve instant upgrades (expected to be a Gnosis multisig)
          /// @param approvedBySecurityCouncil Indicates whether the security council has approved the upgrade
          /// @param proposedUpgradeTimestamp The timestamp when the upgrade was proposed, zero if there are no active proposals
          /// @param currentProposalId The serial number of proposed upgrades, increments when proposing a new one
          struct UpgradeStorage {
              bytes32 proposedUpgradeHash;
              UpgradeState state;
              address securityCouncil;
              bool approvedBySecurityCouncil;
              uint40 proposedUpgradeTimestamp;
              uint40 currentProposalId;
          }
          /// @dev The log passed from L2
          /// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for the future
          /// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address.
          /// This field is required formally but does not have any special meaning.
          /// @param txNumberInBlock The L2 transaction number in a block, in which the log was sent
          /// @param sender The L2 address which sent the log
          /// @param key The 32 bytes of information that was sent in the log
          /// @param value The 32 bytes of information that was sent in the log
          // Both `key` and `value` are arbitrary 32-bytes selected by the log sender
          struct L2Log {
              uint8 l2ShardId;
              bool isService;
              uint16 txNumberInBlock;
              address sender;
              bytes32 key;
              bytes32 value;
          }
          /// @dev An arbitrary length message passed from L2
          /// @notice Under the hood it is `L2Log` sent from the special system L2 contract
          /// @param txNumberInBlock The L2 transaction number in a block, in which the message was sent
          /// @param sender The address of the L2 account from which the message was passed
          /// @param data An arbitrary length message
          struct L2Message {
              uint16 txNumberInBlock;
              address sender;
              bytes data;
          }
          /// @notice Part of the configuration parameters of ZKP circuits
          struct VerifierParams {
              bytes32 recursionNodeLevelVkHash;
              bytes32 recursionLeafLevelVkHash;
              bytes32 recursionCircuitsSetVksHash;
          }
          /// @dev storing all storage variables for zkSync facets
          /// NOTE: It is used in a proxy, so it is possible to add new variables to the end
          /// but NOT to modify already existing variables or change their order.
          /// NOTE: variables prefixed with '__DEPRECATED_' are deprecated and shouldn't be used.
          /// Their presence is maintained for compatibility and to prevent storage collision.
          struct AppStorage {
              /// @dev Storage of variables needed for deprecated diamond cut facet
              uint256[7] __DEPRECATED_diamondCutStorage;
              /// @notice Address which will exercise governance over the network i.e. change validator set, conduct upgrades
              address governor;
              /// @notice Address that the governor proposed as one that will replace it
              address pendingGovernor;
              /// @notice List of permitted validators
              mapping(address => bool) validators;
              /// @dev Verifier contract. Used to verify aggregated proof for blocks
              Verifier verifier;
              /// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis)
              uint256 totalBlocksExecuted;
              /// @notice Total number of proved blocks i.e. blocks[totalBlocksProved] points at the latest proved block
              uint256 totalBlocksVerified;
              /// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block
              uint256 totalBlocksCommitted;
              /// @dev Stored hashed StoredBlock for block number
              mapping(uint256 => bytes32) storedBlockHashes;
              /// @dev Stored root hashes of L2 -> L1 logs
              mapping(uint256 => bytes32) l2LogsRootHashes;
              /// @dev Container that stores transactions requested from L1
              PriorityQueue.Queue priorityQueue;
              /// @dev The smart contract that manages the list with permission to call contract functions
              IAllowList allowList;
              /// @notice Part of the configuration parameters of ZKP circuits. Used as an input for the verifier smart contract
              VerifierParams verifierParams;
              /// @notice Bytecode hash of bootloader program.
              /// @dev Used as an input to zkp-circuit.
              bytes32 l2BootloaderBytecodeHash;
              /// @notice Bytecode hash of default account (bytecode for EOA).
              /// @dev Used as an input to zkp-circuit.
              bytes32 l2DefaultAccountBytecodeHash;
              /// @dev Indicates that the porter may be touched on L2 transactions.
              /// @dev Used as an input to zkp-circuit.
              bool zkPorterIsAvailable;
              /// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions
              /// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e.
              /// without overhead for proving the block.
              uint256 priorityTxMaxGasLimit;
              /// @dev Storage of variables needed for upgrade facet
              UpgradeStorage upgrades;
              /// @dev A mapping L2 block number => message number => flag.
              /// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as
              /// a flag to indicate that the message was already processed.
              /// @dev Used to indicate that eth withdrawal was already processed
              mapping(uint256 => mapping(uint256 => bool)) isEthWithdrawalFinalized;
              /// @dev The most recent withdrawal time and amount reset
              uint256 __DEPRECATED_lastWithdrawalLimitReset;
              /// @dev The accumulated withdrawn amount during the withdrawal limit window
              uint256 __DEPRECATED_withdrawnAmountInWindow;
              /// @dev A mapping user address => the total deposited amount by the user
              mapping(address => uint256) totalDepositedAmountPerUser;
              /// @dev Stores the protocol version. Note, that the protocol version may not only encompass changes to the
              /// smart contracts, but also to the node behavior.
              uint256 protocolVersion;
              /// @dev Hash of the system contract upgrade transaction. If 0, then no upgrade transaction needs to be done.
              bytes32 l2SystemContractsUpgradeTxHash;
              /// @dev Block number where the upgrade transaction has happened. If 0, then no upgrade transaction has happened yet.
              uint256 l2SystemContractsUpgradeBlockNumber;
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./Plonk4VerifierWithAccessToDNext.sol";
          import "../common/libraries/UncheckedMath.sol";
          contract Verifier is Plonk4VerifierWithAccessToDNext {
              using UncheckedMath for uint256;
              function get_verification_key() public pure returns (VerificationKey memory vk) {
                  vk.num_inputs = 1;
                  vk.domain_size = 67108864;
                  vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97);
                  // coefficients
                  vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
                      0x14c289d746e37aa82ec428491881c4732766492a8bc2e8e3cca2000a40c0ea27,
                      0x2f617a7eb9808ad9843d1e080b7cfbf99d61bb1b02076c905f31adb12731bc41
                  );
                  vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
                      0x210b5cc8e6a85d63b65b701b8fb5ad24ff9c41f923432de17fe4ebae04526a8c,
                      0x05c10ab17ea731b2b87fb890fa5b10bd3d6832917a616b807a9b640888ebc731
                  );
                  vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
                      0x29d4d14adcfe67a2ac690d6369db6b75e82d8ab3124bc4fa1dd145f41ca6949c,
                      0x004f6cd229373f1c1f735ccf49aef6a5c32025bc36c3328596dd0db7d87bef67
                  );
                  vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
                      0x06d15382e8cabae9f98374a9fbdadd424f48e24da7e4c65bf710fd7d7d59a05a,
                      0x22e438ad5c51673879ce17073a3d2d29327a97dc3ce61c4f88540e00087695f6
                  );
                  vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
                      0x274a668dfc485cf192d0086f214146d9e02b3040a5a586df344c53c16a87882b,
                      0x15f5bb7ad01f162b70fc77c8ea456d67d15a6ce98acbbfd521222810f8ec0a66
                  );
                  vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
                      0x0ba53bf4fb0446927857e33978d02abf45948fc68f4091394ae0827a22cf1e47,
                      0x0720d818751ce5b3f11c716e925f60df4679ea90bed516499bdec066f5ff108f
                  );
                  vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
                      0x2e986ba2ea495e5ec6af532980b1dc567f1430bfa82f8de07c12fc097c0e0483,
                      0x1555d189f6164e82d78de1b8313c2e923e616b3c8ed0e350c3b61c94516d0b58
                  );
                  vk.gate_setup_commitments[7] = PairingsBn254.new_g1(
                      0x0925959592604ca73c917f9b2e029aa2563c318ddcc5ca29c11badb7b880127b,
                      0x2b4a430fcb2fa7d6d67d6c358e01cf0524c7df7e1e56442f65b39bc1a1052367
                  );
                  // gate selectors
                  vk.gate_selectors_commitments[0] = PairingsBn254.new_g1(
                      0x28f2a0a95af79ba67e9dd1986bd3190199f661b710a693fc82fb395c126edcbd,
                      0x0db75db5de5192d1ba1c24710fc00da16fa8029ac7fe82d855674dcd6d090e05
                  );
                  vk.gate_selectors_commitments[1] = PairingsBn254.new_g1(
                      0x143471a174dfcb2d9cb5ae621e519387bcc93c9dcfc011160b2f5c5f88e32cbe,
                      0x2a0194c0224c3d964223a96c4c99e015719bc879125aa0df3f0715d154e71a31
                  );
                  // permutation
                  vk.permutation_commitments[0] = PairingsBn254.new_g1(
                      0x1423fa82e00ba22c280181afb12c56eea541933eeb5ec39119b0365b6beab4b9,
                      0x0efdcd3423a38f5e2ecf8c7e4fd46f13189f8fed392ad9d8d393e8ba568b06e4
                  );
                  vk.permutation_commitments[1] = PairingsBn254.new_g1(
                      0x0e9b5b12c1090d62224e64aa1696c009aa59a9c3eec458e781fae773e1f4eca5,
                      0x1fe3df508c7e9750eb37d9cae5e7437ad11a21fa36530ff821b407b165a79a55
                  );
                  vk.permutation_commitments[2] = PairingsBn254.new_g1(
                      0x25d1a714bd1e258f196e38d6b2826153382c2d04b870d0b7ec250296005129ae,
                      0x0883a121b41ca7beaa9de97ecf4417e62aa2eeb9434f24ddacbfed57cbf016a8
                  );
                  vk.permutation_commitments[3] = PairingsBn254.new_g1(
                      0x2f3ede68e854a6b3b14589851cf077a606e2aeb3205c43cc579b7abae39d8f58,
                      0x178ccd4b1f78fd79ee248e376b6fc8297d5450900d1e15e8c03e3ed2c171ac8c
                  );
                  // lookup table commitments
                  vk.lookup_selector_commitment = PairingsBn254.new_g1(
                      0x1f814e2d87c332e964eeef94ec695eef9d2caaac58b682a43da5107693b06f30,
                      0x196d56fb01907e66af9303886fd95328d398e5b2b72906882a9d12c1718e2ee2
                  );
                  vk.lookup_tables_commitments[0] = PairingsBn254.new_g1(
                      0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c,
                      0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b
                  );
                  vk.lookup_tables_commitments[1] = PairingsBn254.new_g1(
                      0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9,
                      0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca
                  );
                  vk.lookup_tables_commitments[2] = PairingsBn254.new_g1(
                      0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44,
                      0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765
                  );
                  vk.lookup_tables_commitments[3] = PairingsBn254.new_g1(
                      0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e,
                      0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e
                  );
                  vk.lookup_table_type_commitment = PairingsBn254.new_g1(
                      0x2f85df2d6249ccbcc11b91727333cc800459de6ee274f29c657c8d56f6f01563,
                      0x088e1df178c47116a69c3c8f6d0c5feb530e2a72493694a623b1cceb7d44a76c
                  );
                  // non residues
                  vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005);
                  vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007);
                  vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a);
                  // g2 elements
                  vk.g2_elements[0] = PairingsBn254.new_g2(
                      [
                          0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                          0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                      ],
                      [
                          0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                          0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                      ]
                  );
                  vk.g2_elements[1] = PairingsBn254.new_g2(
                      [
                          0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                          0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
                      ],
                      [
                          0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                          0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
                      ]
                  );
              }
              function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
                  internal
                  pure
                  returns (Proof memory proof)
              {
                  require(serialized_proof.length == 44);
                  proof.input_values = new uint256[](public_inputs.length);
                  for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) {
                      proof.input_values[i] = public_inputs[i];
                  }
                  uint256 j;
                  for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                      proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked(
                          serialized_proof[j],
                          serialized_proof[j.uncheckedInc()]
                      );
                      j = j.uncheckedAdd(2);
                  }
                  proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                      proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked(
                          serialized_proof[j],
                          serialized_proof[j.uncheckedInc()]
                      );
                      j = j.uncheckedAdd(2);
                  }
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
                      proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
                      proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.opening_proof_at_z = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
              }
              function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
                  public
                  view
                  returns (bool)
              {
                  VerificationKey memory vk = get_verification_key();
                  require(vk.num_inputs == public_inputs.length);
                  Proof memory proof = deserialize_proof(public_inputs, serialized_proof);
                  return verify(proof, vk);
              }
          }
          

          File 4 of 4: Verifier
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          library UncheckedMath {
              function uncheckedInc(uint256 _number) internal pure returns (uint256) {
                  unchecked {
                      return _number + 1;
                  }
              }
              function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
                  unchecked {
                      return _lhs + _rhs;
                  }
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          library PairingsBn254 {
              uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
              uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
              uint256 constant bn254_b_coeff = 3;
              struct G1Point {
                  uint256 X;
                  uint256 Y;
              }
              struct Fr {
                  uint256 value;
              }
              function new_fr(uint256 fr) internal pure returns (Fr memory) {
                  require(fr < r_mod);
                  return Fr({value: fr});
              }
              function copy(Fr memory self) internal pure returns (Fr memory n) {
                  n.value = self.value;
              }
              function assign(Fr memory self, Fr memory other) internal pure {
                  self.value = other.value;
              }
              function inverse(Fr memory fr) internal view returns (Fr memory) {
                  require(fr.value != 0);
                  return pow(fr, r_mod - 2);
              }
              function add_assign(Fr memory self, Fr memory other) internal pure {
                  self.value = addmod(self.value, other.value, r_mod);
              }
              function sub_assign(Fr memory self, Fr memory other) internal pure {
                  self.value = addmod(self.value, r_mod - other.value, r_mod);
              }
              function mul_assign(Fr memory self, Fr memory other) internal pure {
                  self.value = mulmod(self.value, other.value, r_mod);
              }
              function pow(Fr memory self, uint256 power) internal view returns (Fr memory) {
                  uint256[6] memory input = [32, 32, 32, self.value, power, r_mod];
                  uint256[1] memory result;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20)
                  }
                  require(success);
                  return Fr({value: result[0]});
              }
              // Encoding of field elements is: X[0] * z + X[1]
              struct G2Point {
                  uint256[2] X;
                  uint256[2] Y;
              }
              function P1() internal pure returns (G1Point memory) {
                  return G1Point(1, 2);
              }
              function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) {
                  return G1Point(x, y);
              }
              // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
              function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) {
                  if (x == 0 && y == 0) {
                      // point of infinity is (0,0)
                      return G1Point(x, y);
                  }
                  // check encoding
                  require(x < q_mod, "x axis isn't valid");
                  require(y < q_mod, "y axis isn't valid");
                  // check on curve
                  uint256 lhs = mulmod(y, y, q_mod); // y^2
                  uint256 rhs = mulmod(x, x, q_mod); // x^2
                  rhs = mulmod(rhs, x, q_mod); // x^3
                  rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b
                  require(lhs == rhs, "is not on curve");
                  return G1Point(x, y);
              }
              function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) {
                  return G2Point(x, y);
              }
              function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) {
                  result.X = self.X;
                  result.Y = self.Y;
              }
              function P2() internal pure returns (G2Point memory) {
                  // for some reason ethereum expects to have c1*v + c0 form
                  return
                      G2Point(
                          [
                              0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                              0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                          ],
                          [
                              0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                              0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                          ]
                      );
              }
              function negate(G1Point memory self) internal pure {
                  // The prime q in the base field F_q for G1
                  if (self.Y == 0) {
                      require(self.X == 0);
                      return;
                  }
                  self.Y = q_mod - self.Y;
              }
              function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
                  point_add_into_dest(p1, p2, r);
                  return r;
              }
              function point_add_assign(G1Point memory p1, G1Point memory p2) internal view {
                  point_add_into_dest(p1, p2, p1);
              }
              function point_add_into_dest(
                  G1Point memory p1,
                  G1Point memory p2,
                  G1Point memory dest
              ) internal view {
                  if (p2.X == 0 && p2.Y == 0) {
                      // we add zero, nothing happens
                      dest.X = p1.X;
                      dest.Y = p1.Y;
                      return;
                  } else if (p1.X == 0 && p1.Y == 0) {
                      // we add into zero, and we add non-zero point
                      dest.X = p2.X;
                      dest.Y = p2.Y;
                      return;
                  } else {
                      uint256[4] memory input;
                      input[0] = p1.X;
                      input[1] = p1.Y;
                      input[2] = p2.X;
                      input[3] = p2.Y;
                      bool success;
                      assembly {
                          success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
                      }
                      require(success);
                  }
              }
              function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view {
                  point_sub_into_dest(p1, p2, p1);
              }
              function point_sub_into_dest(
                  G1Point memory p1,
                  G1Point memory p2,
                  G1Point memory dest
              ) internal view {
                  if (p2.X == 0 && p2.Y == 0) {
                      // we subtracted zero, nothing happens
                      dest.X = p1.X;
                      dest.Y = p1.Y;
                      return;
                  } else if (p1.X == 0 && p1.Y == 0) {
                      // we subtract from zero, and we subtract non-zero point
                      dest.X = p2.X;
                      dest.Y = q_mod - p2.Y;
                      return;
                  } else {
                      uint256[4] memory input;
                      input[0] = p1.X;
                      input[1] = p1.Y;
                      input[2] = p2.X;
                      input[3] = q_mod - p2.Y;
                      bool success = false;
                      assembly {
                          success := staticcall(gas(), 6, input, 0x80, dest, 0x40)
                      }
                      require(success);
                  }
              }
              function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) {
                  // https://eips.ethereum.org/EIPS/eip-197
                  // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0)
                  if (p.X == 0 && p.Y == 1) {
                      p.Y = 0;
                  }
                  point_mul_into_dest(p, s, r);
                  return r;
              }
              function point_mul_assign(G1Point memory p, Fr memory s) internal view {
                  point_mul_into_dest(p, s, p);
              }
              function point_mul_into_dest(
                  G1Point memory p,
                  Fr memory s,
                  G1Point memory dest
              ) internal view {
                  uint256[3] memory input;
                  input[0] = p.X;
                  input[1] = p.Y;
                  input[2] = s.value;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 7, input, 0x60, dest, 0x40)
                  }
                  require(success);
              }
              function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
                  require(p1.length == p2.length);
                  uint256 elements = p1.length;
                  uint256 inputSize = elements * 6;
                  uint256[] memory input = new uint256[](inputSize);
                  for (uint256 i = 0; i < elements; ) {
                      input[i * 6 + 0] = p1[i].X;
                      input[i * 6 + 1] = p1[i].Y;
                      input[i * 6 + 2] = p2[i].X[0];
                      input[i * 6 + 3] = p2[i].X[1];
                      input[i * 6 + 4] = p2[i].Y[0];
                      input[i * 6 + 5] = p2[i].Y[1];
                      unchecked {
                          ++i;
                      }
                  }
                  uint256[1] memory out;
                  bool success;
                  assembly {
                      success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
                  }
                  require(success);
                  return out[0] != 0;
              }
              /// Convenience method for a pairing check for two pairs.
              function pairingProd2(
                  G1Point memory a1,
                  G2Point memory a2,
                  G1Point memory b1,
                  G2Point memory b2
              ) internal view returns (bool) {
                  G1Point[] memory p1 = new G1Point[](2);
                  G2Point[] memory p2 = new G2Point[](2);
                  p1[0] = a1;
                  p1[1] = b1;
                  p2[0] = a2;
                  p2[1] = b2;
                  return pairing(p1, p2);
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./PairingsBn254.sol";
          library TranscriptLib {
              // flip                    0xe000000000000000000000000000000000000000000000000000000000000000;
              uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
              uint32 constant DST_0 = 0;
              uint32 constant DST_1 = 1;
              uint32 constant DST_CHALLENGE = 2;
              struct Transcript {
                  bytes32 state_0;
                  bytes32 state_1;
                  uint32 challenge_counter;
              }
              function new_transcript() internal pure returns (Transcript memory t) {
                  t.state_0 = bytes32(0);
                  t.state_1 = bytes32(0);
                  t.challenge_counter = 0;
              }
              function update_with_u256(Transcript memory self, uint256 value) internal pure {
                  bytes32 old_state_0 = self.state_0;
                  self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value));
                  self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value));
              }
              function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure {
                  update_with_u256(self, value.value);
              }
              function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure {
                  update_with_u256(self, p.X);
                  update_with_u256(self, p.Y);
              }
              function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) {
                  bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter));
                  self.challenge_counter += 1;
                  challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK});
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./libraries/PairingsBn254.sol";
          import "./libraries/TranscriptLib.sol";
          import "../common/libraries/UncheckedMath.sol";
          uint256 constant STATE_WIDTH = 4;
          uint256 constant NUM_G2_ELS = 2;
          struct VerificationKey {
              uint256 domain_size;
              uint256 num_inputs;
              PairingsBn254.Fr omega;
              PairingsBn254.G1Point[2] gate_selectors_commitments;
              PairingsBn254.G1Point[8] gate_setup_commitments;
              PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments;
              PairingsBn254.G1Point lookup_selector_commitment;
              PairingsBn254.G1Point[4] lookup_tables_commitments;
              PairingsBn254.G1Point lookup_table_type_commitment;
              PairingsBn254.Fr[STATE_WIDTH - 1] non_residues;
              PairingsBn254.G2Point[NUM_G2_ELS] g2_elements;
          }
          contract Plonk4VerifierWithAccessToDNext {
              using PairingsBn254 for PairingsBn254.G1Point;
              using PairingsBn254 for PairingsBn254.G2Point;
              using PairingsBn254 for PairingsBn254.Fr;
              using TranscriptLib for TranscriptLib.Transcript;
              using UncheckedMath for uint256;
              struct Proof {
                  uint256[] input_values;
                  // commitments
                  PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments;
                  PairingsBn254.G1Point copy_permutation_grand_product_commitment;
                  PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments;
                  // openings
                  PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z;
                  PairingsBn254.Fr[1] state_polys_openings_at_z_omega;
                  PairingsBn254.Fr[1] gate_selectors_openings_at_z;
                  PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z;
                  PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega;
                  PairingsBn254.Fr quotient_poly_opening_at_z;
                  PairingsBn254.Fr linearization_poly_opening_at_z;
                  // lookup commitments
                  PairingsBn254.G1Point lookup_s_poly_commitment;
                  PairingsBn254.G1Point lookup_grand_product_commitment;
                  // lookup openings
                  PairingsBn254.Fr lookup_s_poly_opening_at_z_omega;
                  PairingsBn254.Fr lookup_grand_product_opening_at_z_omega;
                  PairingsBn254.Fr lookup_t_poly_opening_at_z;
                  PairingsBn254.Fr lookup_t_poly_opening_at_z_omega;
                  PairingsBn254.Fr lookup_selector_poly_opening_at_z;
                  PairingsBn254.Fr lookup_table_type_poly_opening_at_z;
                  PairingsBn254.G1Point opening_proof_at_z;
                  PairingsBn254.G1Point opening_proof_at_z_omega;
              }
              struct PartialVerifierState {
                  PairingsBn254.Fr zero;
                  PairingsBn254.Fr alpha;
                  PairingsBn254.Fr beta;
                  PairingsBn254.Fr gamma;
                  PairingsBn254.Fr[9] alpha_values;
                  PairingsBn254.Fr eta;
                  PairingsBn254.Fr beta_lookup;
                  PairingsBn254.Fr gamma_lookup;
                  PairingsBn254.Fr beta_plus_one;
                  PairingsBn254.Fr beta_gamma;
                  PairingsBn254.Fr v;
                  PairingsBn254.Fr u;
                  PairingsBn254.Fr z;
                  PairingsBn254.Fr z_omega;
                  PairingsBn254.Fr z_minus_last_omega;
                  PairingsBn254.Fr l_0_at_z;
                  PairingsBn254.Fr l_n_minus_one_at_z;
                  PairingsBn254.Fr t;
                  PairingsBn254.G1Point tp;
              }
              function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at)
                  internal
                  view
                  returns (PairingsBn254.Fr memory num)
              {
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size);
                  PairingsBn254.Fr memory den = at.copy();
                  den.sub_assign(one);
                  den.mul_assign(size_fe);
                  den = den.inverse();
                  num = at.pow(domain_size);
                  num.sub_assign(one);
                  num.mul_assign(den);
              }
              function evaluate_lagrange_poly_out_of_domain(
                  uint256 poly_num,
                  uint256 domain_size,
                  PairingsBn254.Fr memory omega,
                  PairingsBn254.Fr memory at
              ) internal view returns (PairingsBn254.Fr memory res) {
                  // (omega^i / N) / (X - omega^i) * (X^N - 1)
                  require(poly_num < domain_size);
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory omega_power = omega.pow(poly_num);
                  res = at.pow(domain_size);
                  res.sub_assign(one);
                  require(res.value != 0); // Vanishing polynomial cannot be zero at point `at`
                  res.mul_assign(omega_power);
                  PairingsBn254.Fr memory den = PairingsBn254.copy(at);
                  den.sub_assign(omega_power);
                  den.mul_assign(PairingsBn254.new_fr(domain_size));
                  den = den.inverse();
                  res.mul_assign(den);
              }
              function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at)
                  internal
                  view
                  returns (PairingsBn254.Fr memory res)
              {
                  res = at.pow(domain_size);
                  res.sub_assign(PairingsBn254.new_fr(1));
              }
              function initialize_transcript(Proof memory proof, VerificationKey memory vk)
                  internal
                  pure
                  returns (PartialVerifierState memory state)
              {
                  TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript();
                  for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
                      transcript.update_with_u256(proof.input_values[i]);
                  }
                  for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                      transcript.update_with_g1(proof.state_polys_commitments[i]);
                  }
                  state.eta = transcript.get_challenge();
                  transcript.update_with_g1(proof.lookup_s_poly_commitment);
                  state.beta = transcript.get_challenge();
                  state.gamma = transcript.get_challenge();
                  transcript.update_with_g1(proof.copy_permutation_grand_product_commitment);
                  state.beta_lookup = transcript.get_challenge();
                  state.gamma_lookup = transcript.get_challenge();
                  transcript.update_with_g1(proof.lookup_grand_product_commitment);
                  state.alpha = transcript.get_challenge();
                  for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                      transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]);
                  }
                  state.z = transcript.get_challenge();
                  transcript.update_with_fr(proof.quotient_poly_opening_at_z);
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.state_polys_openings_at_z[i]);
                  }
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]);
                  }
                  for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]);
                  }
                  for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]);
                  }
                  state.z_omega = state.z.copy();
                  state.z_omega.mul_assign(vk.omega);
                  transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega);
                  transcript.update_with_fr(proof.lookup_t_poly_opening_at_z);
                  transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z);
                  transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z);
                  transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega);
                  transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega);
                  transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega);
                  transcript.update_with_fr(proof.linearization_poly_opening_at_z);
                  state.v = transcript.get_challenge();
                  transcript.update_with_g1(proof.opening_proof_at_z);
                  transcript.update_with_g1(proof.opening_proof_at_z_omega);
                  state.u = transcript.get_challenge();
              }
              // compute some powers of challenge alpha([alpha^1, .. alpha^8])
              function compute_powers_of_alpha(PartialVerifierState memory state) public pure {
                  require(state.alpha.value != 0);
                  state.alpha_values[0] = PairingsBn254.new_fr(1);
                  state.alpha_values[1] = state.alpha.copy();
                  PairingsBn254.Fr memory current_alpha = state.alpha.copy();
                  for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) {
                      current_alpha.mul_assign(state.alpha);
                      state.alpha_values[i] = current_alpha.copy();
                  }
              }
              function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) {
                  // we initialize all challenges beforehand, we can draw each challenge in its own place
                  PartialVerifierState memory state = initialize_transcript(proof, vk);
                  if (verify_quotient_evaluation(vk, proof, state) == false) {
                      return false;
                  }
                  require(proof.state_polys_openings_at_z_omega.length == 1);
                  PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1();
                  {
                      // block scope
                      PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size);
                      PairingsBn254.Fr memory current_z = z_in_domain_size.copy();
                      PairingsBn254.G1Point memory tp;
                      // start from i =1
                      for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                          tp = proof.quotient_poly_parts_commitments[i].copy_g1();
                          tp.point_mul_assign(current_z);
                          quotient_result.point_add_assign(tp);
                          current_z.mul_assign(z_in_domain_size);
                      }
                  }
                  Queries memory queries = prepare_queries(vk, proof, state);
                  queries.commitments_at_z[0] = quotient_result;
                  queries.values_at_z[0] = proof.quotient_poly_opening_at_z;
                  queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state);
                  queries.values_at_z[1] = proof.linearization_poly_opening_at_z;
                  require(queries.commitments_at_z.length == queries.values_at_z.length);
                  PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0];
                  PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0];
                  PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1);
                  PairingsBn254.G1Point memory scaled;
                  for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) {
                      aggregation_challenge.mul_assign(state.v);
                      scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge);
                      aggregated_commitment_at_z.point_add_assign(scaled);
                      state.t = queries.values_at_z[i];
                      state.t.mul_assign(aggregation_challenge);
                      aggregated_opening_at_z.add_assign(state.t);
                  }
                  aggregation_challenge.mul_assign(state.v);
                  PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul(
                      aggregation_challenge
                  );
                  PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0];
                  aggregated_opening_at_z_omega.mul_assign(aggregation_challenge);
                  for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) {
                      aggregation_challenge.mul_assign(state.v);
                      scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge);
                      aggregated_commitment_at_z_omega.point_add_assign(scaled);
                      state.t = queries.values_at_z_omega[i];
                      state.t.mul_assign(aggregation_challenge);
                      aggregated_opening_at_z_omega.add_assign(state.t);
                  }
                  return
                      final_pairing(
                          vk.g2_elements,
                          proof,
                          state,
                          aggregated_commitment_at_z,
                          aggregated_commitment_at_z_omega,
                          aggregated_opening_at_z,
                          aggregated_opening_at_z_omega
                      );
              }
              function verify_quotient_evaluation(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) internal view returns (bool) {
                  uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs);
                  for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) {
                      lagrange_poly_numbers[i] = i;
                  }
                  require(vk.num_inputs > 0);
                  PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0);
                  for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) {
                      state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z);
                      state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i]));
                      inputs_term.add_assign(state.t);
                  }
                  inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]);
                  PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy();
                  result.add_assign(inputs_term);
                  // compute powers of alpha
                  compute_powers_of_alpha(state);
                  PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
                  factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
                  // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega)
                  require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1);
                  PairingsBn254.Fr memory t; // TMP;
                  for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      t = proof.copy_permutation_polys_openings_at_z[i].copy();
                      t.mul_assign(state.beta);
                      t.add_assign(proof.state_polys_openings_at_z[i]);
                      t.add_assign(state.gamma);
                      factor.mul_assign(t);
                  }
                  t = proof.state_polys_openings_at_z[3].copy();
                  t.add_assign(state.gamma);
                  factor.mul_assign(t);
                  result.sub_assign(factor);
                  // - L_0(z) * alpha_1
                  PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z);
                  l_0_at_z.mul_assign(state.alpha_values[4 + 1]);
                  result.sub_assign(l_0_at_z);
                  PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state);
                  result.add_assign(lookup_quotient_contrib);
                  PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy();
                  lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z));
                  return lhs.value == result.value;
              }
              function lookup_quotient_contribution(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) internal view returns (PairingsBn254.Fr memory result) {
                  PairingsBn254.Fr memory t;
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  state.beta_plus_one = state.beta_lookup.copy();
                  state.beta_plus_one.add_assign(one);
                  state.beta_gamma = state.beta_plus_one.copy();
                  state.beta_gamma.mul_assign(state.gamma_lookup);
                  // (s'*beta + gamma)*(zw')*alpha
                  t = proof.lookup_s_poly_opening_at_z_omega.copy();
                  t.mul_assign(state.beta_lookup);
                  t.add_assign(state.beta_gamma);
                  t.mul_assign(proof.lookup_grand_product_opening_at_z_omega);
                  t.mul_assign(state.alpha_values[6]);
                  // (z - omega^{n-1}) for this part
                  PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1);
                  state.z_minus_last_omega = state.z.copy();
                  state.z_minus_last_omega.sub_assign(last_omega);
                  t.mul_assign(state.z_minus_last_omega);
                  result.add_assign(t);
                  // - alpha_1 * L_{0}(z)
                  state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
                  t = state.l_0_at_z.copy();
                  t.mul_assign(state.alpha_values[6 + 1]);
                  result.sub_assign(t);
                  // - alpha_2 * beta_gamma_powered L_{n-1}(z)
                  PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1);
                  state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain(
                      vk.domain_size - 1,
                      vk.domain_size,
                      vk.omega,
                      state.z
                  );
                  t = state.l_n_minus_one_at_z.copy();
                  t.mul_assign(beta_gamma_powered);
                  t.mul_assign(state.alpha_values[6 + 2]);
                  result.sub_assign(t);
              }
              function aggregated_linearization_commitment(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) internal view returns (PairingsBn254.G1Point memory result) {
                  // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next)
                  result = PairingsBn254.new_g1(0, 0);
                  // Q_a * A
                  PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul(
                      proof.state_polys_openings_at_z[0]
                  );
                  result.point_add_assign(scaled);
                  // Q_b * B
                  scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]);
                  result.point_add_assign(scaled);
                  // Q_c * C
                  scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]);
                  result.point_add_assign(scaled);
                  // Q_d * D
                  scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]);
                  result.point_add_assign(scaled);
                  // Q_m* A*B or Q_ab*A*B
                  PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy();
                  t.mul_assign(proof.state_polys_openings_at_z[1]);
                  scaled = vk.gate_setup_commitments[4].point_mul(t);
                  result.point_add_assign(scaled);
                  // Q_AC* A*C
                  t = proof.state_polys_openings_at_z[0].copy();
                  t.mul_assign(proof.state_polys_openings_at_z[2]);
                  scaled = vk.gate_setup_commitments[5].point_mul(t);
                  result.point_add_assign(scaled);
                  // Q_const
                  result.point_add_assign(vk.gate_setup_commitments[6]);
                  // Q_dNext * D_next
                  scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]);
                  result.point_add_assign(scaled);
                  result.point_mul_assign(proof.gate_selectors_openings_at_z[0]);
                  PairingsBn254.G1Point
                      memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution(
                          vk,
                          proof,
                          state
                      );
                  result.point_add_assign(rescue_custom_gate_linearization_contrib);
                  require(vk.non_residues.length == STATE_WIDTH - 1);
                  PairingsBn254.Fr memory one = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory factor = state.alpha_values[4].copy();
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) {
                      t = state.z.copy();
                      if (i == 0) {
                          t.mul_assign(one);
                      } else {
                          t.mul_assign(vk.non_residues[i - 1]);
                      }
                      t.mul_assign(state.beta);
                      t.add_assign(state.gamma);
                      t.add_assign(proof.state_polys_openings_at_z[i]);
                      factor.mul_assign(t);
                      unchecked {
                          ++i;
                      }
                  }
                  scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
                  // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X)
                  factor = state.alpha_values[4].copy();
                  factor.mul_assign(state.beta);
                  factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega);
                  for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                      t = proof.copy_permutation_polys_openings_at_z[i].copy();
                      t.mul_assign(state.beta);
                      t.add_assign(state.gamma);
                      t.add_assign(proof.state_polys_openings_at_z[i]);
                      factor.mul_assign(t);
                  }
                  scaled = vk.permutation_commitments[3].point_mul(factor);
                  result.point_sub_assign(scaled);
                  // + L_0(z) * Z(x)
                  state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z);
                  require(state.l_0_at_z.value != 0);
                  factor = state.l_0_at_z.copy();
                  factor.mul_assign(state.alpha_values[4 + 1]);
                  scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
                  PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state);
                  result.point_add_assign(lookup_linearization_contrib);
              }
              function rescue_custom_gate_linearization_contribution(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) public view returns (PairingsBn254.G1Point memory result) {
                  PairingsBn254.Fr memory t;
                  PairingsBn254.Fr memory intermediate_result;
                  // a^2 - b = 0
                  t = proof.state_polys_openings_at_z[0].copy();
                  t.mul_assign(t);
                  t.sub_assign(proof.state_polys_openings_at_z[1]);
                  // t.mul_assign(challenge1);
                  t.mul_assign(state.alpha_values[1]);
                  intermediate_result.add_assign(t);
                  // b^2 - c = 0
                  t = proof.state_polys_openings_at_z[1].copy();
                  t.mul_assign(t);
                  t.sub_assign(proof.state_polys_openings_at_z[2]);
                  t.mul_assign(state.alpha_values[1 + 1]);
                  intermediate_result.add_assign(t);
                  // c*a - d = 0;
                  t = proof.state_polys_openings_at_z[2].copy();
                  t.mul_assign(proof.state_polys_openings_at_z[0]);
                  t.sub_assign(proof.state_polys_openings_at_z[3]);
                  t.mul_assign(state.alpha_values[1 + 2]);
                  intermediate_result.add_assign(t);
                  result = vk.gate_selectors_commitments[1].point_mul(intermediate_result);
              }
              function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state)
                  internal
                  view
                  returns (PairingsBn254.G1Point memory result)
              {
                  PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0);
                  PairingsBn254.Fr memory t;
                  PairingsBn254.Fr memory factor;
                  // s(x) from the Z(x*omega)*(\\gamma*(1 + \\beta) + s(x) + \\beta * s(x*omega)))
                  factor = proof.lookup_grand_product_opening_at_z_omega.copy();
                  factor.mul_assign(state.alpha_values[6]);
                  factor.mul_assign(state.z_minus_last_omega);
                  PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
                  // Z(x) from - alpha_0 * Z(x) * (\\beta + 1) * (\\gamma + f(x)) * (\\gamma(1 + \\beta) + t(x) + \\beta * t(x*omega))
                  // + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z)
                  // accumulate coefficient
                  factor = proof.lookup_t_poly_opening_at_z_omega.copy();
                  factor.mul_assign(state.beta_lookup);
                  factor.add_assign(proof.lookup_t_poly_opening_at_z);
                  factor.add_assign(state.beta_gamma);
                  // (\\gamma + f(x))
                  PairingsBn254.Fr memory f_reconstructed;
                  PairingsBn254.Fr memory current = PairingsBn254.new_fr(1);
                  PairingsBn254.Fr memory tmp0;
                  for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                      tmp0 = proof.state_polys_openings_at_z[i].copy();
                      tmp0.mul_assign(current);
                      f_reconstructed.add_assign(tmp0);
                      current.mul_assign(state.eta);
                  }
                  // add type of table
                  t = proof.lookup_table_type_poly_opening_at_z.copy();
                  t.mul_assign(current);
                  f_reconstructed.add_assign(t);
                  f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z);
                  f_reconstructed.add_assign(state.gamma_lookup);
                  // end of (\\gamma + f(x)) part
                  factor.mul_assign(f_reconstructed);
                  factor.mul_assign(state.beta_plus_one);
                  t = zero.copy();
                  t.sub_assign(factor);
                  factor = t;
                  factor.mul_assign(state.alpha_values[6]);
                  // Multiply by (z - omega^{n-1})
                  factor.mul_assign(state.z_minus_last_omega);
                  // L_{0}(z) in front of Z(x)
                  t = state.l_0_at_z.copy();
                  t.mul_assign(state.alpha_values[6 + 1]);
                  factor.add_assign(t);
                  // L_{n-1}(z) in front of Z(x)
                  t = state.l_n_minus_one_at_z.copy();
                  t.mul_assign(state.alpha_values[6 + 2]);
                  factor.add_assign(t);
                  scaled = proof.lookup_grand_product_commitment.point_mul(factor);
                  result.point_add_assign(scaled);
              }
              struct Queries {
                  PairingsBn254.G1Point[13] commitments_at_z;
                  PairingsBn254.Fr[13] values_at_z;
                  PairingsBn254.G1Point[6] commitments_at_z_omega;
                  PairingsBn254.Fr[6] values_at_z_omega;
              }
              function prepare_queries(
                  VerificationKey memory vk,
                  Proof memory proof,
                  PartialVerifierState memory state
              ) public view returns (Queries memory queries) {
                  // we set first two items in calee side so start idx from 2
                  uint256 idx = 2;
                  for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                      queries.commitments_at_z[idx] = proof.state_polys_commitments[i];
                      queries.values_at_z[idx] = proof.state_polys_openings_at_z[i];
                      idx = idx.uncheckedInc();
                  }
                  require(proof.gate_selectors_openings_at_z.length == 1);
                  queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0];
                  queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0];
                  idx = idx.uncheckedInc();
                  for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) {
                      queries.commitments_at_z[idx] = vk.permutation_commitments[i];
                      queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i];
                      idx = idx.uncheckedInc();
                  }
                  queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment;
                  queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1];
                  queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega;
                  queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0];
                  PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0];
                  PairingsBn254.Fr memory current_eta = state.eta.copy();
                  for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) {
                      state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta);
                      lookup_t_poly_commitment_aggregated.point_add_assign(state.tp);
                      current_eta.mul_assign(state.eta);
                  }
                  queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated;
                  queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z;
                  idx = idx.uncheckedInc();
                  queries.commitments_at_z[idx] = vk.lookup_selector_commitment;
                  queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z;
                  idx = idx.uncheckedInc();
                  queries.commitments_at_z[idx] = vk.lookup_table_type_commitment;
                  queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z;
                  queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment;
                  queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega;
                  queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment;
                  queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega;
                  queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated;
                  queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega;
              }
              function final_pairing(
                  // VerificationKey memory vk,
                  PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements,
                  Proof memory proof,
                  PartialVerifierState memory state,
                  PairingsBn254.G1Point memory aggregated_commitment_at_z,
                  PairingsBn254.G1Point memory aggregated_commitment_at_z_omega,
                  PairingsBn254.Fr memory aggregated_opening_at_z,
                  PairingsBn254.Fr memory aggregated_opening_at_z_omega
              ) internal view returns (bool) {
                  // q(x) = f(x) - f(z) / (x - z)
                  // q(x) * (x-z)  = f(x) - f(z)
                  // f(x)
                  PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1();
                  aggregated_commitment_at_z_omega.point_mul_assign(state.u);
                  pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega);
                  // - f(z)*g
                  PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy();
                  aggregated_value.mul_assign(state.u);
                  aggregated_value.add_assign(aggregated_opening_at_z);
                  PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value);
                  pair_with_generator.point_sub_assign(tp);
                  // +z * q(x)
                  tp = proof.opening_proof_at_z.point_mul(state.z);
                  PairingsBn254.Fr memory t = state.z_omega.copy();
                  t.mul_assign(state.u);
                  PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t);
                  tp.point_add_assign(t1);
                  pair_with_generator.point_add_assign(tp);
                  // rhs
                  PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u);
                  pair_with_x.point_add_assign(proof.opening_proof_at_z);
                  pair_with_x.negate();
                  // Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]`
                  // so we handle it in code generation step
                  PairingsBn254.G2Point memory first_g2 = g2_elements[0];
                  PairingsBn254.G2Point memory second_g2 = g2_elements[1];
                  return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2);
              }
          }
          pragma solidity ^0.8.13;
          // SPDX-License-Identifier: MIT
          import "./Plonk4VerifierWithAccessToDNext.sol";
          import "../common/libraries/UncheckedMath.sol";
          contract Verifier is Plonk4VerifierWithAccessToDNext {
              using UncheckedMath for uint256;
              function get_verification_key() public pure returns (VerificationKey memory vk) {
                  vk.num_inputs = 1;
                  vk.domain_size = 67108864;
                  vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97);
                  // coefficients
                  vk.gate_setup_commitments[0] = PairingsBn254.new_g1(
                      0x14c289d746e37aa82ec428491881c4732766492a8bc2e8e3cca2000a40c0ea27,
                      0x2f617a7eb9808ad9843d1e080b7cfbf99d61bb1b02076c905f31adb12731bc41
                  );
                  vk.gate_setup_commitments[1] = PairingsBn254.new_g1(
                      0x210b5cc8e6a85d63b65b701b8fb5ad24ff9c41f923432de17fe4ebae04526a8c,
                      0x05c10ab17ea731b2b87fb890fa5b10bd3d6832917a616b807a9b640888ebc731
                  );
                  vk.gate_setup_commitments[2] = PairingsBn254.new_g1(
                      0x29d4d14adcfe67a2ac690d6369db6b75e82d8ab3124bc4fa1dd145f41ca6949c,
                      0x004f6cd229373f1c1f735ccf49aef6a5c32025bc36c3328596dd0db7d87bef67
                  );
                  vk.gate_setup_commitments[3] = PairingsBn254.new_g1(
                      0x06d15382e8cabae9f98374a9fbdadd424f48e24da7e4c65bf710fd7d7d59a05a,
                      0x22e438ad5c51673879ce17073a3d2d29327a97dc3ce61c4f88540e00087695f6
                  );
                  vk.gate_setup_commitments[4] = PairingsBn254.new_g1(
                      0x274a668dfc485cf192d0086f214146d9e02b3040a5a586df344c53c16a87882b,
                      0x15f5bb7ad01f162b70fc77c8ea456d67d15a6ce98acbbfd521222810f8ec0a66
                  );
                  vk.gate_setup_commitments[5] = PairingsBn254.new_g1(
                      0x0ba53bf4fb0446927857e33978d02abf45948fc68f4091394ae0827a22cf1e47,
                      0x0720d818751ce5b3f11c716e925f60df4679ea90bed516499bdec066f5ff108f
                  );
                  vk.gate_setup_commitments[6] = PairingsBn254.new_g1(
                      0x2e986ba2ea495e5ec6af532980b1dc567f1430bfa82f8de07c12fc097c0e0483,
                      0x1555d189f6164e82d78de1b8313c2e923e616b3c8ed0e350c3b61c94516d0b58
                  );
                  vk.gate_setup_commitments[7] = PairingsBn254.new_g1(
                      0x0925959592604ca73c917f9b2e029aa2563c318ddcc5ca29c11badb7b880127b,
                      0x2b4a430fcb2fa7d6d67d6c358e01cf0524c7df7e1e56442f65b39bc1a1052367
                  );
                  // gate selectors
                  vk.gate_selectors_commitments[0] = PairingsBn254.new_g1(
                      0x28f2a0a95af79ba67e9dd1986bd3190199f661b710a693fc82fb395c126edcbd,
                      0x0db75db5de5192d1ba1c24710fc00da16fa8029ac7fe82d855674dcd6d090e05
                  );
                  vk.gate_selectors_commitments[1] = PairingsBn254.new_g1(
                      0x143471a174dfcb2d9cb5ae621e519387bcc93c9dcfc011160b2f5c5f88e32cbe,
                      0x2a0194c0224c3d964223a96c4c99e015719bc879125aa0df3f0715d154e71a31
                  );
                  // permutation
                  vk.permutation_commitments[0] = PairingsBn254.new_g1(
                      0x1423fa82e00ba22c280181afb12c56eea541933eeb5ec39119b0365b6beab4b9,
                      0x0efdcd3423a38f5e2ecf8c7e4fd46f13189f8fed392ad9d8d393e8ba568b06e4
                  );
                  vk.permutation_commitments[1] = PairingsBn254.new_g1(
                      0x0e9b5b12c1090d62224e64aa1696c009aa59a9c3eec458e781fae773e1f4eca5,
                      0x1fe3df508c7e9750eb37d9cae5e7437ad11a21fa36530ff821b407b165a79a55
                  );
                  vk.permutation_commitments[2] = PairingsBn254.new_g1(
                      0x25d1a714bd1e258f196e38d6b2826153382c2d04b870d0b7ec250296005129ae,
                      0x0883a121b41ca7beaa9de97ecf4417e62aa2eeb9434f24ddacbfed57cbf016a8
                  );
                  vk.permutation_commitments[3] = PairingsBn254.new_g1(
                      0x2f3ede68e854a6b3b14589851cf077a606e2aeb3205c43cc579b7abae39d8f58,
                      0x178ccd4b1f78fd79ee248e376b6fc8297d5450900d1e15e8c03e3ed2c171ac8c
                  );
                  // lookup table commitments
                  vk.lookup_selector_commitment = PairingsBn254.new_g1(
                      0x1f814e2d87c332e964eeef94ec695eef9d2caaac58b682a43da5107693b06f30,
                      0x196d56fb01907e66af9303886fd95328d398e5b2b72906882a9d12c1718e2ee2
                  );
                  vk.lookup_tables_commitments[0] = PairingsBn254.new_g1(
                      0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c,
                      0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b
                  );
                  vk.lookup_tables_commitments[1] = PairingsBn254.new_g1(
                      0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9,
                      0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca
                  );
                  vk.lookup_tables_commitments[2] = PairingsBn254.new_g1(
                      0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44,
                      0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765
                  );
                  vk.lookup_tables_commitments[3] = PairingsBn254.new_g1(
                      0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e,
                      0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e
                  );
                  vk.lookup_table_type_commitment = PairingsBn254.new_g1(
                      0x2f85df2d6249ccbcc11b91727333cc800459de6ee274f29c657c8d56f6f01563,
                      0x088e1df178c47116a69c3c8f6d0c5feb530e2a72493694a623b1cceb7d44a76c
                  );
                  // non residues
                  vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005);
                  vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007);
                  vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a);
                  // g2 elements
                  vk.g2_elements[0] = PairingsBn254.new_g2(
                      [
                          0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2,
                          0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed
                      ],
                      [
                          0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b,
                          0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa
                      ]
                  );
                  vk.g2_elements[1] = PairingsBn254.new_g2(
                      [
                          0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1,
                          0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0
                      ],
                      [
                          0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4,
                          0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55
                      ]
                  );
              }
              function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
                  internal
                  pure
                  returns (Proof memory proof)
              {
                  require(serialized_proof.length == 44);
                  proof.input_values = new uint256[](public_inputs.length);
                  for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) {
                      proof.input_values[i] = public_inputs[i];
                  }
                  uint256 j;
                  for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) {
                      proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked(
                          serialized_proof[j],
                          serialized_proof[j.uncheckedInc()]
                      );
                      j = j.uncheckedAdd(2);
                  }
                  proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) {
                      proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked(
                          serialized_proof[j],
                          serialized_proof[j.uncheckedInc()]
                      );
                      j = j.uncheckedAdd(2);
                  }
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) {
                      proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) {
                      proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) {
                      proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]);
                      j = j.uncheckedInc();
                  }
                  proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]);
                  j = j.uncheckedInc();
                  proof.opening_proof_at_z = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
                  j = j.uncheckedAdd(2);
                  proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked(
                      serialized_proof[j],
                      serialized_proof[j.uncheckedInc()]
                  );
              }
              function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof)
                  public
                  view
                  returns (bool)
              {
                  VerificationKey memory vk = get_verification_key();
                  require(vk.num_inputs == public_inputs.length);
                  Proof memory proof = deserialize_proof(public_inputs, serialized_proof);
                  return verify(proof, vk);
              }
          }