Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x803bd6a0577c083c1EDe82Da455C8e69e697c878
Balance 0 ETH
Nonce 1
Code Size 24480 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

24480 bytes
0x60806040526004361015610011575f80fd5b5f3560e01c806302cfb563146104ed5780630b4d1558146104e85780630b9a9eb0146104e357806317010c68146104de5780631b06a763146104d95780631b6592fa146104d457806322b22256146104cf57806323ff50e1146104ca57806325216fda146104c557806325394645146104c057806326012922146104bb5780632d320e28146104b65780632f25807e146104b157806330f72b0a146104ac5780633177a48e146104a757806331c5da4e146104a2578063348e8a931461039e5780633c40c6761461049d5780633eec953814610498578063437545f914610493578063439fab911461048e57806345dca05c146104895780634ae71a21146103a857806355a2ba681461048457806361d027b31461047f578063634f02621461047a57806364420e05146104755780636883bde6146104435780636997fb5e146103a8578063706d6fbe146103a857806371175ff9146104705780637271277e1461046b57806372fc4c391461046657806379c929c1146104615780637b1a49091461045c5780637de213eb146104575780638096edf2146103d05780638542fb1414610443578063899cfa29146104525780638a8570831461044d57806390fda39b1461044857806393371c1a146104435780639588eca21461043e578063975364c61461043957806399e51881146104345780639d5aeec5146103a8578063a1922dfc146103cb578063a29141961461039e578063a44a93161461042f578063a4b6f7561461042a578063aab78a8e14610425578063aabe607814610420578063abf6a0381461041b578063ae150b3914610416578063af7c026014610411578063b1e09d451461040c578063b60cbbd714610407578063ba279a4914610402578063baa08f7d146103fd578063bdf723e8146103f8578063bfda3066146103a8578063c157483d146103f3578063cd565e08146103ee578063cd626497146103e9578063d0c40555146103e4578063d1cbc64f146103df578063d20191bd146103da578063d5102eea146103d5578063d925e21f146103d0578063da16ec10146103cb578063db085664146103c6578063e415f0f4146103c1578063e64d8795146103bc578063e8b2f939146103b7578063e9560055146103b2578063ea2102e4146103ad578063f051e760146103a8578063f0f44260146103a3578063f22b50ff1461039e578063fae2e046146103995763ff6b936c14610394575f80fd5b61418e565b614166565b61202d565b614017565b6121e3565b613fb2565b613f45565b613f2b565b613f05565b613755565b61372c565b612832565b612542565b613703565b6136cc565b613662565b61362d565b6132e8565b613248565b61305d565b612b71565b612b4b565b612b2f565b612b0f565b612aee565b612aa2565b612a6f565b612a2b565b6128c2565b6128a2565b612889565b61284d565b612802565b612617565b6125fb565b6123e3565b6125d5565b6125ad565b61255d565b612527565b6124b4565b612498565b61246f565b612434565b612405565b6123bd565b612394565b61236e565b6121fe565b6121c6565b6120e5565b6120c5565b6120a8565b61204d565b612004565b611fe7565b611fab565b611bf4565b611746565b6116bc565b611528565b61147c565b610ebf565b610c71565b6108ad565b610656565b61060d565b610598565b610550565b610500565b5f9103126104fc57565b5f80fd5b346104fc575f3660031901126104fc57602060ff600f54166040519015158152f35b6004359060ff821682036104fc57565b6024359060ff821682036104fc57565b908160609103126104fc5790565b346104fc5760803660031901126104fc57610569610522565b50610572610532565b506064356001600160401b0381116104fc57610592903690600401610542565b50614ea2565b346104fc575f3660031901126104fc57602060405163ffffffff8152f35b6004359065ffffffffffff821682036104fc57565b6024359065ffffffffffff821682036104fc57565b9181601f840112156104fc578235916001600160401b0383116104fc57602083818601950101116104fc57565b346104fc5760603660031901126104fc576106266105b6565b5061062f610532565b506044356001600160401b0381116104fc5761064f9036906004016105e0565b5050614ea2565b346104fc575f3660031901126104fc57602060095460c01c604051908152f35b6001600160401b038116036104fc57565b6004359061069482610676565b565b6024359061069482610676565b6064359061069482610676565b6084359061069482610676565b6044359061069482610676565b60a4359061069482610676565b359061069482610676565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161070957604052565b6106e2565b604081019081106001600160401b0382111761070957604052565b61018081019081106001600160401b0382111761070957604052565b90601f801991011681019081106001600160401b0382111761070957604052565b6040519061016082018281106001600160401b0382111761070957604052565b6040519060c082018281106001600160401b0382111761070957604052565b604051906106948261070e565b6001600160401b0381116107095760051b60200190565b6001600160401b03811161070957601f01601f191660200190565b9291926107f0826107c9565b916107fe6040519384610745565b8294818452818301116104fc578281602093845f960137010152565b9080601f830112156104fc57813591602091610835846107b2565b9360406108456040519687610745565b818652848087019260051b850101938385116104fc57858101925b858410610871575050505050505090565b83356001600160401b0381116104fc57820185603f820112156104fc5787916108a2878387868096013591016107e4565b815201930192610860565b346104fc576040806003193601126104fc576004908135906108ce82610676565b6001600160401b03926024358481116104fc576108ee903690830161081a565b6108f6614f20565b610909610905600f5460ff1690565b1590565b610c6257600854858160801c1680158015610c58575b610c3057868616908111908115610c24575b50610c15578590841c165f9481610be9575b925f9491949085925b610965610959878961420c565b6001600160401b031690565b8985161015610b445788831695606461097e888861424d565b5151118015610b31575b610b2357610994614261565b975f5b6109a1898961424d565b51518110156109fd57806109ea6109c46001936109be8d8d61424d565b5161428c565b517fff000000000000000000000000000000000000000000000000000000000000001690565b5f1a6109f6828d61428c565b5301610997565b5094939197610a2c610a1e919a9294989a8a5192839160208301958661429d565b03601f198101835282610745565b51902097610a4b846001600160401b03165f52600260205260405f2090565b548903610afb5789816029610a7f610a79610a736109c4610a6d8a988e61424d565b5161423b565b60f81c90565b60ff1690565b14610aaa575b5090610959916001610a99610965956142b6565b96011695989250505094909461094c565b9150506026610ab9828861424d565b515103610afb57828a6001610a9961096595610af084610ae5610adf610959998f61424d565b51614f91565b9290919216916150c8565b955050509091610a85565b8688517f196d115f000000000000000000000000000000000000000000000000000000008152fd5b5051631cc51aa360e31b8152fd5b50610b3c878761424d565b515115610988565b610bdd610baa87610b96610b6d82610b686008546001600160401b039060801c1690565b6141db565b67ffffffffffffffff60801b1967ffffffffffffffff60801b6008549260801b16911617600855565b60085460401c6001600160401b031661420c565b6fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006008549260401b16911617600855565b610be76001601755565b005b9450610c0e610bf7826141be565b6001600160401b03165f52600260205260405f2090565b5494610943565b828451631cc51aa360e31b8152fd5b9050825114155f610931565b8385517fc756d15e000000000000000000000000000000000000000000000000000000008152fd5b508686161561091f565b50905163036e45ad60e21b8152fd5b346104fc575f3660031901126104fc57610c89614f20565b6020610c936142ce565b60016017556040519015158152f35b63ffffffff8116036104fc57565b6044359061069482610ca2565b60a4359061069482610ca2565b6064359061069482610ca2565b60c4359061069482610ca2565b359061069482610ca2565b6101609060031901126104fc57610d04610766565b90610d0d610687565b8252610d17610696565b6020830152610d24610cb0565b6040830152610d316106a3565b6060830152610d3e6106b0565b6080830152610d4b610cbd565b60a083015260c43560c083015260e43560e0830152610104356101008301526101243561012083015261014435610140830152565b6101609060231901126104fc57610d95610766565b90610d9e610696565b8252610da86106bd565b6020830152610db5610cca565b6040830152610dc26106b0565b6060830152610dcf6106ca565b6080830152610ddc610cd7565b60a083015260e43560c08301526101043560e0830152610124356101008301526101443561012083015261016435610140830152565b9190826101609103126104fc57610e27610766565b91610e31816106d7565b8352610e3f602082016106d7565b6020840152610e5060408201610ce4565b6040840152610e61606082016106d7565b6060840152610e72608082016106d7565b6080840152610e8360a08201610ce4565b60a084015260c081013560c084015260e081013560e0840152610100808201359084015261012080820135908401526101408091013590830152565b346104fc576101803660031901126104fc57610eda36610cef565b6001600160401b0390610164358281116104fc57610efd600491369083016105e0565b929091610f08614f20565b60ff600f54166113e757610f33610f276006546001600160a01b031690565b6001600160a01b031690565b93843b156104fc576040945f86518092631015428760e21b82528180610f6b338983019190916001600160a01b036020820193169052565b03915afa8015611379576113ce575b5082516001600160401b031693610f996009546001600160401b031690565b9487610fa7610959886141f4565b9116036113a657610fb78461514b565b610fe2610fcb86516001600160401b031690565b6001600160401b03165f52600d60205260405f2090565b540361137e5761106191602091610ff76143cb565b916110276101408801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b6110308461423b565b52611045610f2787546001600160a01b031690565b91895195869485938493633f27bd4560e11b85528a8501614420565b03915afa908115611379575f9161134a575b5015611323575061112e61111261109183516001600160401b031690565b937f5c836e1ff20ea85c52b6e3d2ef0124d3304bf3b37cc8fb0e2c84ae7d44c0593e6110c38786015163ffffffff1690565b602086019661110a6110dc89516001600160401b031690565b8a516001600160401b03938416815263ffffffff909416602085015291909116604083015281906060820190565b0390a16142b6565b6001600160401b03166001600160401b03196009541617600955565b61117e61116261115161114860a085015163ffffffff1690565b63ffffffff1690565b6008546001600160401b031661420c565b6001600160401b03166001600160401b03196008541617600855565b6101206101008201916111918351600a55565b019061119d8251600b55565b6111cd6111b184516001600160401b031690565b6001600160401b03166001600160401b0319600c541617600c55565b60095494808660801c161580156112e8575b6111ed57610be76001601755565b61125b61127993610b6d93611252847f5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb3255119a166fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b515f5551600155565b6008549061126f8183881c168284166141db565b9160801c166141db565b61128e610baa6008546001600160401b031690565b6112da6112b86112aa6009546001600160401b039060401c1690565b92516001600160401b031690565b92516001600160401b0392831681529190921660208201529081906040820190565b0390a15f8080808080610bdd565b5061131761130a8760c01c6001600160401b03165f52600e60205260405f2090565b546001600160401b031690565b818088169116116111df565b83517fc39805c6000000000000000000000000000000000000000000000000000000008152fd5b61136c915060203d602011611372575b6113648183610745565b810190614408565b5f611073565b503d61135a565b6143c0565b8286517ffa98ffb1000000000000000000000000000000000000000000000000000000008152fd5b8286517fa3449f4c000000000000000000000000000000000000000000000000000000008152fd5b806113db6113e1926106f6565b806104f2565b5f610f7a565b60405163a74f50b360e01b8152fd5b6004359061ffff821682036104fc57565b6024359061ffff821682036104fc57565b6044359061ffff821682036104fc57565b6001600160a01b038116036104fc57565b604435906001600160801b03821682036104fc57565b602435906001600160801b03821682036104fc57565b606435906001600160801b03821682036104fc57565b346104fc5760e03660031901126104fc576114956113f6565b602435906114a282611429565b6044359160ff831683036104fc5760643566ffffffffffffff811681036104fc57608435906001600160801b03821682036104fc57610be79460a435936114e885610676565b60c435956114f587610676565b614485565b60206003198201126104fc57600435906001600160401b0382116104fc57611524916004016105e0565b9091565b346104fc57611536366114fa565b61153e614f20565b6001600160a01b0391827f000000000000000000000000803bd6a0577c083c1ede82da455c8e69e697c878163014611692577fb11bb680dab3fb9238aa513aacc13268188c937de0f36b523827f6716396917461159c3684846107e4565b602081519101200361160e576115b78184938493019061496c565b939194909416931691169180611665575b5080611638575b50806115df57610be76001601755565b803b1561160e57611608906001600160a01b036101ee91166001600160a01b0319825416179055565b5f610bdd565b60046040517ffc2bb126000000000000000000000000000000000000000000000000000000008152fd5b803b1561160e5761165f906001600160a01b03166001600160a01b03196005541617600555565b5f6115cf565b803b1561160e5761168c906001600160a01b03166001600160a01b03196007541617600755565b5f6115c8565b60046040517f647f3cb3000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc576020604051603e8152f35b81601f820112156104fc5780359060206116f0836107b2565b936116fe6040519586610745565b83855260208501906020610160809602850101938185116104fc57602001915b84831061172e5750505050505090565b83869161173b8486610e12565b81520192019161171e565b346104fc576040806003193601126104fc576004906001600160401b0382358181116104fc5761177990369085016116d7565b6024358281116104fc57611790903690860161081a565b90611799614f20565b60ff600f5416611be4578051825103611bbb578051946009956117ca61095988546001600160401b039060801c1690565b10611b94575f5b82518110156119f2576117f56117e7828561424d565b51516001600160401b031690565b87548682169087811682116119ca5761095961130a6118289260c01c6001600160401b03165f52600e60205260405f2090565b036119a2579061199c610b6d89610b6860019561185a611848878b61424d565b51611853888d61424d565b5190615208565b61195e61197361196e61186e865460c01c90565b956119166118a0611890896001600160401b03165f52600e60205260405f2090565b5460401c6001600160401b031690565b956118e3610baa6118906118cb60089a6118c58c546001600160401b039060401c1690565b906141db565b9b6001600160401b03165f52600e60205260405f2090565b6fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b61195e61192c611927835460c01c90565b6142b6565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b03196009549260c01b16911617600955565b5460801c6001600160401b031690565b6149a2565b67ffffffffffffffff60801b1967ffffffffffffffff60801b6009549260801b16911617600955565b016117d1565b8287517f516e3eb1000000000000000000000000000000000000000000000000000000008152fd5b8489517fedb9c87f000000000000000000000000000000000000000000000000000000008152fd5b7f5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb32551183878988610100611a2d611a2786516149b7565b8661424d565b5101515f55611a53610120611a4b611a4587516149b7565b8761424d565b510151600155565b815493818560801c16158015611b66575b15611b095750610b6d81611aae611ad293611ae79697166fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b600a545f55611abe600b54600155565b6008549061126f8183891c168284166141db565b611890610baa6008546001600160401b031690565b611afc6112b8600c546001600160401b031690565b0390a1610be76001601755565b611b5e92506020611b2782611b21611b3694516149b7565b9061424d565b5101516001600160401b031690565b9083519485941c16839060209093929360408301946001600160401b03809216845216910152565b0390a1610bdd565b50611b8861130a8660c01c6001600160401b03165f52600e60205260405f2090565b82808716911611611a64565b84517f81b1c191000000000000000000000000000000000000000000000000000000008152fd5b505050517f40068547000000000000000000000000000000000000000000000000000000008152fd5b5050505163a74f50b360e01b8152fd5b346104fc5760603660031901126104fc5760048035611c1281611429565b611c1a611407565b91611c2361143a565b90611c2c614f20565b611c3583615529565b90611c76611c6983611c548861ffff165f526101f260205260405f2090565b9065ffffffffffff165f5260205260405f2090565b546001600160801b031690565b926001600160801b038085169116908082118015611fa3575b611f9357611cb2611cad8861ffff165f526101f060205260405f2090565b6149d2565b600161ffff89161490811580611f72575b611f62579081611d10611cfb60608b950196611cf5611ce989516001600160801b031690565b6001600160801b031690565b90614a71565b94611cf5611ce988516001600160801b031690565b9115611ece575050506020611d6891604051809381927f7b1a49090000000000000000000000000000000000000000000000000000000083528a888401602090939291936001600160a01b0360408201951681520152565b03815f305af1908115611379575f91611e9f575b50915b611d9c611d96611ce984516001600160801b031690565b846143f9565b611e77575092611e427fef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f9593611e1e611e06611dff611dfa611e6a97611df4611ce96001600160a01b039c516001600160801b031690565b90614a93565b615582565b8095614a9d565b91611c548a61ffff165f526101f260205260405f2090565b906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6040519384931695839092916001600160801b0360209161ffff604085019616845216910152565b0390a2610be76001601755565b6040517fd301c22e000000000000000000000000000000000000000000000000000000008152fd5b611ec1915060203d602011611ec7575b611eb98183610745565b810190614a84565b5f611d7c565b503d611eaf565b90611f28611ee9610f2760209594516001600160a01b031690565b916040519586948594630ab4574d60e31b86528a8601909260609295949360808301966001600160a01b03809216845216602083015260408201520152565b03815f305af1908115611379575f91611f43575b5091611d7f565b611f5c915060203d602011611ec757611eb98183610745565b5f611f3c565b8460405163344c14a160e11b8152fd5b506001600160a01b03611f8c82516001600160a01b031690565b1615611cc3565b8260405163bb8f9df560e01b8152fd5b508115611c8f565b346104fc5760203660031901126104fc576001600160401b03600435611fd081610676565b165f526015602052602060405f2054604051908152f35b346104fc575f3660031901126104fc576020600a54604051908152f35b346104fc575f3660031901126104fc5760206001600160401b0360095460801c16604051908152f35b346104fc575f3660031901126104fc57602060405165ffffffffffff8152f35b346104fc5760c03660031901126104fc576120666105b6565b5061206f611407565b5060443565ffffffffffff8116036104fc5761208c606435610ca2565b60843560ff8116036104fc5760a43560ff811614614ea2575f80fd5b346104fc575f3660031901126104fc576020600b54604051908152f35b346104fc575f3660031901126104fc57602060405165fffffffffffe8152f35b346104fc5761213a6120f6366114fa565b6016929192549261211e60ff8560081c1615809581966121b8575b8115612198575b50614ab6565b83612131600160ff196016541617601655565b61217f57614b79565b61214057005b61215061ff001960165416601655565b604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602090a1005b61219361010061ff00196016541617601655565b614b79565b303b159150816121aa575b505f612118565b6001915060ff16145f6121a3565b600160ff8216109150612111565b346104fc575f3660031901126104fc576020600154604051908152f35b346104fc575f3660031901126104fc57602060405160018152f35b346104fc5760803660031901126104fc5760043561221b81611429565b60243561222781611429565b303303612344576040517f70a08231000000000000000000000000000000000000000000000000000000008082523060048301526020936001600160a01b038116939291908583602481885afa9384156113795786935f9561231f575b50906122939160443591615907565b60405190815230600482015292839060249082905afa918215611379576122c2935f93612300575b50506149c5565b60643581116122d657604051908152602090f35b60046040517f03a77b73000000000000000000000000000000000000000000000000000000008152fd5b612317929350803d10611ec757611eb98183610745565b905f806122bb565b6122939291955061233c90853d8711611ec757611eb98183610745565b949091612284565b60046040517fd463e24c000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc5760206001600160a01b0360115416604051908152f35b346104fc575f3660031901126104fc5760206008546001600160401b036040519160401c168152f35b346104fc575f3660031901126104fc5760206001600160401b0360085416604051908152f35b346104fc575f3660031901126104fc576020604051670fffffffffffffff8152f35b346104fc575f3660031901126104fc576020604051621275008152f35b6101609060231901126104fc57602490565b346104fc5736600319016101c081126104fc57610160136104fc576101a4356001600160401b0381116104fc5761064f9036906004016105e0565b346104fc575f3660031901126104fc5760206001600160401b0360085460801c16604051908152f35b346104fc575f3660031901126104fc576020604051610ffe8152f35b346104fc5760403660031901126104fc576004356124d181611429565b60243590303303612344575f80808481945af16124ec614da7565b50156124fd57602090604051908152f35b60046040517f8e94fb8e000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc57602060405160038152f35b346104fc576080366003190112806104fc5715614ea2575f80fd5b346104fc5760203660031901126104fc576001600160a01b0360043561258281611429565b165f526101f1602052602061ffff60405f205416604051908152f35b6044359060028210156104fc57565b60803660031901126104fc576125c4600435611429565b6125cc611407565b5061059261259e565b346104fc575f3660031901126104fc5760206001600160401b0360095416604051908152f35b346104fc575f3660031901126104fc5760205f54604051908152f35b346104fc5760403660031901126104fc5760043561263481611429565b61263c611450565b612644614f20565b61264d82615529565b9061266b611c698365ffffffffffff165f52601460205260405f2090565b926001600160801b0380851692169180831180156127fa575b6127e95761269d610f276006546001600160a01b031690565b906040518092631f209df760e11b825281600460209586935afa801561137957839285925f926127b6575b50604051630ab4574d60e31b81526001600160a01b039283166004820152919092166024820152604481019590955260648501528380608481015b03815f305af193841561137957611e1e61275d6127567fef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f976001600160a01b0397612774965f92612799575b5050615582565b8098614a9d565b9165ffffffffffff165f52601460205260405f2090565b60408051600381526001600160801b0390951660208601529116929081908101611e6a565b6127af9250803d10611ec757611eb98183610745565b5f8061274f565b6127039192506127db90853d87116127e2575b6127d38183610745565b810190614dd6565b91906126c8565b503d6127c9565b600460405163bb8f9df560e01b8152fd5b508215612684565b346104fc5760203660031901126104fc576004356001600160401b0381116104fc57610592903690600401610542565b346104fc575f3660031901126104fc57602060405160fe8152f35b346104fc5760203660031901126104fc576001600160401b0360043561287281610676565b165f52600d602052602060405f2054604051908152f35b346104fc5760203660031901126104fc576105926105b6565b346104fc575f3660031901126104fc57602060085460c01c604051908152f35b346104fc5760203660031901126104fc576004356128df81611429565b6128e7614f20565b6128fc610f276006546001600160a01b031690565b803b156104fc57604051633d7e13b560e21b8152336004820152905f90829060249082905afa801561137957612a18575b506001600160a01b038116156129ee5765ffffffffffff8061294e83615529565b16036129c457612974906001600160a01b03166001600160a01b03196012541617601255565b7fbbc858f043fabd2c7f56f0751bc461f96ba7e4a8d059fa6507ac6cf51238fa0f611afc6129aa6012546001600160a01b031690565b6040516001600160a01b0390911681529081906020820190565b60046040517fd78db55d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f2acea741000000000000000000000000000000000000000000000000000000008152fd5b806113db612a25926106f6565b5f61292d565b346104fc5760203660031901126104fc576001600160a01b03600435612a5081611429565b165f526013602052602065ffffffffffff60405f205416604051908152f35b346104fc5760603660031901126104fc57612a886105b6565b50612a916105cb565b50612a9d604435610676565b614ea2565b346104fc5760203660031901126104fc5765ffffffffffff612ace600435612ac981611429565b615529565b165f52601460205260206001600160801b0360405f205416604051908152f35b346104fc575f3660031901126104fc5760206040516001600160801b038152f35b346104fc575f3660031901126104fc576020604051657fffffffffff8152f35b346104fc575f3660031901126104fc5760206040516103e88152f35b346104fc575f3660031901126104fc5760206001600160a01b0360125416604051908152f35b346104fc576101803660031901126104fc5760046001600160401b0381358181116104fc57612ba390369084016116d7565b90612bad36610d80565b612bb5614f20565b60ff600f541661304d57612bd4610f276006546001600160a01b031690565b90813b156104fc576040915f83518092631015428760e21b82528180612c0c338c83019190916001600160a01b036020820193169052565b03915afa80156113795761303a575b505f5b845163ffffffff821690811015612efa57612c39908661424d565b51612c5161095960208301516001600160401b031690565b15612ed257600890612c64825460c01c90565b612c7f816001600160401b03165f52600d60205260405f2090565b54612c898361514b565b03612ec35781516001600160401b031687808316911603612ec3576001600160401b03165f908152600d602052604081205560e0810151612df5575b90612db49291612d0e612cdc61196e845460c01c90565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b03196008549260c01b16911617600855565b612d8b60a0820191612d7e612d41612d2d611148865163ffffffff1690565b60075460a01c6001600160401b03166141db565b7fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff67ffffffffffffffff60a01b6007549260a01b16911617600755565b516001600160401b031690565b91612d9e6009546001600160401b031690565b928880851691161115612db9575b505050614deb565b612c1e565b610b68612ddf61114861116294612dd5611112612ded986149a2565b5163ffffffff1690565b91546001600160401b031690565b5f8080612dac565b60098054878160801c16908115612e9b5790612e139160c01c61420c565b612e3961130a612e22836141be565b6001600160401b03165f52600e60205260405f2090565b88612e4e61095986516001600160401b031690565b911603612e8c5761197361196e612db4969594935f612e72612e22612e83966141be565b555460801c6001600160401b031690565b90919250612cc5565b898751637ddbe9e960e11b8152fd5b8a88517fff6996ed000000000000000000000000000000000000000000000000000000008152fd5b888651637ddbe9e960e11b8152fd5b8684517f41fdb37d000000000000000000000000000000000000000000000000000000008152fd5b82848887600854918260c01c600954938385871c168210908115613013575b50612feb57612f39906001600160401b03165f52600d60205260405f2090565b54612f438661514b565b03612fdd577f6d80424573caa7280d1b1d9933dd38c7532f82305e148b3f3a9df551a4c53581611afc8587868680612f8284516001600160401b031690565b9216911614612fa4575b50600854905160c09190911c81529081906020820190565b6111b1602082612fbb610100612fd7950151600a55565b612fc9610120820151600b55565b01516001600160401b031690565b83612f8c565b8351637ddbe9e960e11b8152fd5b5083517fff6996ed000000000000000000000000000000000000000000000000000000008152fd5b9050838061302d6007546001600160401b039060a01c1690565b92881c1691161087612f19565b806113db613047926106f6565b5f612c1b565b8360405163a74f50b360e01b8152fd5b346104fc5760a03660031901126104fc576130766105b6565b61307e6105cb565b613086611418565b9161308f611466565b6084356001600160401b0381116104fc576130ae9036906004016105e0565b906130b7614f20565b6130c6610905600f5460ff1690565b613237576130ef6130e885611c548961ffff165f526101f360205260405f2090565b5460ff1690565b61320d57613177916020916131026143cb565b9161313a613114878b8b8b5f54615a5d565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b6131438461423b565b52613159610f276005546001600160a01b031690565b9160405195869485938493633f27bd4560e11b855260048501614420565b03915afa908115611379575f916131ee575b50156131c457836131a4611c5492610bdd966131b7966150c8565b61ffff165f526101f360205260405f2090565b805460ff19166001179055565b60046040517faf7695bd000000000000000000000000000000000000000000000000000000008152fd5b613207915060203d602011611372576113648183610745565b5f613189565b60046040517fe0ba6d1a000000000000000000000000000000000000000000000000000000008152fd5b600460405163036e45ad60e21b8152fd5b346104fc5760203660031901126104fc5761ffff6132646113f6565b165f9081526101f06020908152604091829020805460019091015483516001600160a01b038316815260a083811c60ff169482019490945260a89290921c66ffffffffffffff16938201939093526001600160801b0383166060820152608083811c6001600160401b03169082015260c092831c9181019190915290819081010390f35b346104fc5760803660031901126104fc576133016113f6565b613309610532565b6044359161331683610676565b6064359261332384610676565b61332b614f20565b60ff600f541661361c5761334a610f276006546001600160a01b031690565b803b156104fc5760408051633d7e13b560e21b81523360048083019190915291925f90829060249082905afa801561137957613609575b5061339c611cad8561ffff165f526101f060205260405f2090565b600161ffff86161415806135e8575b6135d95760ff8616801590811590816135cd575b506135be57806135a4575b613595576001600160401b0391828516928315801561358b575b61357c5783670fffffffffffffff8111918215613570575b50506135625769ffffffffffffffffffff6001600160801b036134418685019561343c613430885166ffffffffffffff1690565b66ffffffffffffff1690565b614813565b161161356257509161352d611afc94926134f3856134e36134b060606134a16134937fff23feaffcab98dc102270f0c98539db1067368280f613f9dfd91a601c20113d9f9b516001600160a01b031690565b965166ffffffffffffff1690565b9301516001600160801b031690565b916134cb6134bc610786565b6001600160a01b039096168652565b60ff8c16602086015266ffffffffffffff1684870152565b6001600160801b03166060830152565b6001600160401b03841660808201526001600160401b03851660a08201526135288761ffff165f526101f060205260405f2090565b614831565b51948594859290949360ff60609361ffff60808701981686521660208501526001600160401b03809216604085015216910152565b835163a28d29a760e01b8152fd5b8a16119050835f6133fc565b50835163a28d29a760e01b8152fd5b50808916156133e4565b50905163a28d29a760e01b8152fd5b50600160ff6135b7602084015160ff1690565b16146133ca565b82845163a28d29a760e01b8152fd5b6001915014155f6133bf565b50905163344c14a160e11b8152fd5b506001600160a01b0361360282516001600160a01b031690565b16156133ab565b806113db613616926106f6565b5f613381565b600460405163a74f50b360e01b8152fd5b346104fc576101003660031901126104fc57613647610522565b50613650610532565b5060a036606319011215614ea2575f80fd5b346104fc5760403660031901126104fc5760206001600160801b036136c260043561368c81611429565b61ffff6136a061369a611407565b92615529565b91165f526101f2845260405f209065ffffffffffff165f5260205260405f2090565b5416604051908152f35b346104fc5760803660031901126104fc576136e56105b6565b506136ee611407565b506136f761259e565b50612a9d606435610676565b346104fc575f3660031901126104fc5760206009546001600160401b036040519160401c168152f35b346104fc575f3660031901126104fc5760206001600160401b0360075460a01c16604051908152f35b346104fc57600319610180368201126104fc576004908135916001600160401b038084116104fc5783820192610140809186360301126104fc5761379836612422565b906137a1614f20565b60ff600f541661304d576137c0610f276006546001600160a01b031690565b95863b156104fc576040965f88518092631015428760e21b825281806137f8338c83019190916001600160a01b036020820193169052565b03915afa801561137957613ef2575b506101248101936138188588614e00565b905015613ee35760ff613860610a7361383a613834898c614e00565b90614e32565b357fff000000000000000000000000000000000000000000000000000000000000001690565b16613ebb5761386e87614e3b565b956020850196826138816109598a614e3b565b91161115613e945761389b61389589614e3b565b97614e3b565b96826138ba610959602487019a6138b46111488d614e45565b9061420c565b911603613e6d5760448301946138cf86614e3b565b836138df61095960808501614e3b565b9116108015613e4b575b613e2357600854968760c01c92613911846001600160401b03165f52600d60205260405f2090565b5461392461391f3686610e12565b61514b565b03613e155760075460a01c6001600160401b031698858d8160848a019c61394a8e614e45565b63ffffffff166139599161420c565b93818160801c16921c169061396d9161420c565b8183169182911610613ded57600111159081613dd1575b50613daa5750928a96949192898989956101006139b78f9b996139aa6139b1918e614e00565b8091614e4f565b90615b26565b910135936139d7816001600160401b03165f526101ed60205260405f2090565b54613d7c575b612cdc6139e9916142b6565b60c48601359485613c8e575b506139ff8a614e3b565b90613a0984614e45565b90613a138b614e3b565b6064890193613a2185614e3b565b9260e48b01359b8b8b6101048f9e01359e8f90613a3d8c614e45565b94516001600160c01b031960c098891b8116602083019081527fffffffff0000000000000000000000000000000000000000000000000000000060e09d8e1b811660288501529a8a1b8216602c8401529a891b166034820152603c81019e909e52605c8e0152607c8d0152609c8c015290861b90931660bc8a015260a490920135908801819052838801919091529186529094613adc61010082610745565b51902098600854613aed9060c01c90565b9b613af88d9c614e3b565b94613b0290614e45565b91613b0c90614e3b565b92613b1690614e3b565b93613b2090614e45565b94613b29610766565b6001600160401b03909d168d526001600160401b031660208d015263ffffffff909116908b01526001600160401b031660608a01526001600160401b0316608089015263ffffffff1660a088015260c087015260e0860152610100850152610120840152820152613b999061514b565b90613bb5906001600160401b03165f52600d60205260405f2090565b55613bbf90614e45565b63ffffffff1660075460a01c6001600160401b031690613bde9161420c565b613c1f907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff67ffffffffffffffff60a01b6007549260a01b16911617600755565b60085460c01c90613c2f90614e45565b91613c3990614e3b565b92516001600160401b03918216815263ffffffff92909216602083015290911660408201527f181b25ea9d4d730f30d779f3d2099c03b26b653c889d33eef253d54baaacbd0d90606090a1610be76001601755565b613d5a90613d09612e22613ca460085460c01c90565b92613cf7613ccd613cc16007546001600160401b039060a01c1690565b6138b46111488b614e45565b613ce7613cd86107a5565b6001600160401b039097168752565b6001600160401b03166020860152565b600954908160801c169060c01c61420c565b906001600160401b038151166fffffffffffffffffffffffffffffffff196fffffffffffffffff00000000000000006020855494846001600160401b03198716178755015160401b16921617179055565b613d766119736119276009546001600160401b039060801c1690565b5f6139f5565b93506139e9612cdc613da0866001600160401b03165f526101ed60205260405f2090565b54959150506139dd565b8b517f5a9f2712000000000000000000000000000000000000000000000000000000008152fd5b613ddf9150610bf7906141be565b5460a487013514155f613984565b828e517f49495a1f000000000000000000000000000000000000000000000000000000008152fd5b8b51637ddbe9e960e11b8152fd5b5088517f8c9c6aae000000000000000000000000000000000000000000000000000000008152fd5b50613e5860648501614e3b565b83613e6561095989614e3b565b9116106138e9565b88517f4107ddc8000000000000000000000000000000000000000000000000000000008152fd5b88517f02a546b3000000000000000000000000000000000000000000000000000000008152fd5b8588517f2ccba61c000000000000000000000000000000000000000000000000000000008152fd5b858851630aa47a8360e11b8152fd5b806113db613eff926106f6565b5f613807565b346104fc575f3660031901126104fc5760206001600160401b03600c5416604051908152f35b346104fc575f3660031901126104fc5760206040515f8152f35b346104fc5760203660031901126104fc576001600160401b03600435613f6a81610676565b165f526101ed602052602060405f2054604051908152f35b9181601f840112156104fc578235916001600160401b0383116104fc576020808501948460051b0101116104fc57565b346104fc5760603660031901126104fc576001600160401b036004358181116104fc57613fe3903690600401613f82565b50506024358181116104fc57613ffd903690600401613f82565b50506044359081116104fc5761064f903690600401613f82565b346104fc5760203660031901126104fc5760043561403481611429565b61403c614f20565b614051610f276006546001600160a01b031690565b803b156104fc57604051633d7e13b560e21b8152336004820152905f90829060249082905afa801561137957614153575b506001600160a01b038116156141295765ffffffffffff806140a383615529565b16036140ff576140c9906001600160a01b03166001600160a01b03196011541617601155565b7f8a3509a4057c89a5993a4a3140c2ebf7e829d325d8998eaa6c48adcff98b2cef611afc6129aa6011546001600160a01b031690565b60046040517fe20b3007000000000000000000000000000000000000000000000000000000008152fd5b60046040517f4e5dd6e7000000000000000000000000000000000000000000000000000000008152fd5b806113db614160926106f6565b5f614082565b346104fc575f3660031901126104fc57602065ffffffffffff60125460a01c16604051908152f35b346104fc575f3660031901126104fc5760206040516108008152f35b634e487b7160e01b5f52601160045260245ffd5b6001600160401b039081165f1901919082116141d657565b6141aa565b6001600160401b0391821690821603919082116141d657565b9060016001600160401b03809316019182116141d657565b9190916001600160401b03808094169116019182116141d657565b634e487b7160e01b5f52603260045260245ffd5b8051156142485760200190565b614227565b80518210156142485760209160051b010190565b6040519060a082018281106001600160401b0382111761070957604052606482526080366020840137565b908151811015614248570160200190565b602092839282528051928391018483015e01015f815290565b6001600160401b038091169081146141d65760010190565b6008546001600160401b03808260801c161515918261438d575b8261434c575b50501561434857614304610905600f5460ff1690565b61430d57600190565b61431f600160ff19600f541617600f55565b7f9f7e400a81dddbf1c18b1c37f82aa303d166295ca4b577eb2a7c23d4b704ba895f80a1600190565b5f90565b61438492506143766109599260019260401c166001600160401b03165f52600260205260405f2090565b01546001600160401b031690565b15155f806142ee565b91506143b76109596001614376848660401c166001600160401b03165f52600260205260405f2090565b421015916142e8565b6040513d5f823e3d90fd5b604051906143d88261070e565b6001825260203681840137565b634e487b7160e01b5f52601260045260245ffd5b8115614403570690565b6143e5565b908160209103126104fc575180151581036104fc5790565b9180916040845281604085015260608401375f60608284010152601f801991011681016020608060608301928294836060828403019101528551809452019301915f5b828110614471575050505090565b835185529381019392810192600101614463565b939196959694929094614496614f20565b60ff600f541661361c576144b5610f276006546001600160a01b031690565b803b156104fc5760408051633d7e13b560e21b81523360048083019190915291925f90829060249082905afa801561137957614800575b5061ffff8088166001811480156147c8575b6147b957600181109081156147ae575b506135d9576001600160a01b0389161590811561477c575b506146f15760ff83168015159081614770575b506146f157873b156146f1576001600160801b038086168015801561475f575b801561474e575b6135be5781108015614734575b801561471f575b6135955766ffffffffffffff90818616916145986001600160401b038a1684614813565b908315938415614715575b505082156146ff575b50506146f15750917ff1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c979893916146e495936146506145e9610786565b6001600160a01b038b16815260ff8416602082015266ffffffffffffff8516818401526001600160801b03861660608201526001600160401b03871660808201526001600160401b03881660a08201526135288a61ffff165f526101f060205260405f2090565b614681886146708b6001600160a01b03165f526101f160205260405f2090565b9061ffff1661ffff19825416179055565b5197889788959366ffffffffffffff9060ff60c097939a99956001600160a01b036001600160801b039661ffff60e08d019e168c521660208b01521660408901521660608701521660808501526001600160401b0380921660a085015216910152565b0390a16106946001601755565b905163a28d29a760e01b8152fd5b69ffffffffffffffffffff925016115f806145ac565b1192505f806145a3565b506001600160401b03808816908c1611614574565b50670fffffffffffffff6001600160401b0388161161456d565b506001600160401b038c1615614560565b506001600160401b03881615614559565b6001915014155f614539565b90506147a561479d8a6001600160a01b03165f526101f160205260405f2090565b5461ffff1690565b1615155f614526565b603e9150115f61450e565b82845163344c14a160e11b8152fd5b506001600160a01b036147f86147eb8b61ffff165f526101f060205260405f2090565b546001600160a01b031690565b1615156144fe565b806113db61480d926106f6565b5f6144ec565b9190916001600160801b03808094169116029182169182036141d657565b81518154602084015160408501517fffffffff000000000000000000000000000000000000000000000000000000009092166001600160a01b03939093169290921760a092831b74ff0000000000000000000000000000000000000000161760a89190911b7bffffffffffffff0000000000000000000000000000000000000000001617825560608301516106949360019093019261493c92916148fd906001600160801b031685906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b612fc961491460808301516001600160401b031690565b855467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178555565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b031983549260c01b169116179055565b908160609103126104fc57803561498281611429565b916040602083013561499381611429565b92013561499f81611429565b90565b6001600160401b031680156141d6575f190190565b5f198101919082116141d657565b919082039182116141d657565b9060405160c08101906001600160401b039181811083821117610709576106949260a091604052614a536001849766ffffffffffffff81546001600160a01b038116885260ff81881c16602089015260a81c1660408701520154916001600160801b03831660608601528260801c1660808501906001600160401b03169052565b60c01c910152565b9060a082029180830460a014901517156141d657565b818102929181159184041417156141d657565b908160209103126104fc575190565b8115614403570490565b6001600160801b0391821690821603919082116141d657565b15614abd57565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b91908260c09103126104fc578135614b3e81611429565b916020810135614b4d81611429565b916040820135614b5c81611429565b916060810135614b6b81611429565b9160a0608083013592013590565b6001600160a01b0391827f000000000000000000000000803bd6a0577c083c1ede82da455c8e69e697c878163014614d7d5782614bc381938293614bbb615671565b810190614b27565b98909216979691959290941693169116803b158015614d74575b8015614d6b575b8015614d62575b614d3857614cc295614c39614c5592614c1d614c71966001600160a01b03166001600160a01b03196004541617600455565b6001600160a01b03166001600160a01b03196006541617600655565b6001600160a01b03166001600160a01b03196007541617600755565b6001600160a01b03166001600160a01b03196005541617600555565b614c79610766565b915f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401525f60e0840152816101008401526101208301525f6101408301525f5561514b565b5f8052600d6020527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee55614cf4615692565b610694740200000000000000000000000000000000000000007fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff6012541617601255565b60046040517fc8ba615c000000000000000000000000000000000000000000000000000000008152fd5b50813b15614beb565b50853b15614be4565b50823b15614bdd565b60046040517f19849362000000000000000000000000000000000000000000000000000000008152fd5b3d15614dd1573d90614db8826107c9565b91614dc66040519384610745565b82523d5f602084013e565b606090565b908160209103126104fc575161499f81611429565b63ffffffff8091169081146141d65760010190565b903590601e19813603018212156104fc57018035906001600160401b0382116104fc576020019181360383136104fc57565b90156142485790565b3561499f81610676565b3561499f81610ca2565b90929192836001116104fc5783116104fc57600101915f190190565b906020116104fc5790602090565b906040116104fc5760200190602090565b909392938483116104fc5784116104fc578101920390565b6001600160a01b03807f000000000000000000000000803bd6a0577c083c1ede82da455c8e69e697c878163014614ef657600754165f8060405192368285378336915af4903d91825f833e15614ef457f35bfd5b60046040517fa4a9ab33000000000000000000000000000000000000000000000000000000008152fd5b600260175414614f31576002601755565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b60010190816001116141d657565b5f1981146141d65760010190565b90614fa0600783511015615e62565b600782015190614fb4601d84511015615e62565b614fc2601d84015193615ca4565b929050929190565b65ffffffffffff81165f9081527ff3ea710bd913b11d46a8bb045be6b74773714e429efd3633381568c675975c16602052604090206101f2926001600160801b0391829081905416911601918183116141d6576106949361505c92604051946150328661070e565b16845260ff602085015260035f5260205260405f209065ffffffffffff165f5260205260405f2090565b815181546fffffffffffffffffffffffffffffffff19166001600160801b039190911617815590602001517fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff70ff0000000000000000000000000000000083549260801b169116179055565b9061ffff1691825f526101f291826020526001600160801b039182806151018460405f209065ffffffffffff165f5260205260405f2090565b5416911601928284116141d6576106949461505c93604051956151238761070e565b16855260ff60208601525f5260205260405f209065ffffffffffff165f5260205260405f2090565b60405160208101916151668382516001600160401b03169052565b60208101516001600160401b03166040830152604081015163ffffffff16606083015260608101516001600160401b0316608083015260808101516001600160401b031660a083015260a081015163ffffffff1660c083015260c081015160e083015260e08101516101009081840152810151610120908184015281015190610140918284015201516101609081830152815261520281610729565b51902090565b61521c610fcb82516001600160401b031690565b546152268261514b565b03615518575f9180515f5b8181106152705750505060e001510361524657565b60046040517f8c59c66d000000000000000000000000000000000000000000000000000000008152fd5b60ff61527c8285615cd7565b905016600281145f146153f95750601161529682846149c5565b106153cf576152a59083615d48565b94906153c36152ba825165ffffffffffff1690565b610a1e6020918285019461532161531361530a6152d9895161ffff1690565b936152fe604096878301966152f861095989516001600160401b031690565b916150c8565b5165ffffffffffff1690565b975161ffff1690565b91516001600160401b031690565b915195869485019788927fffff000000000000000000000000000000000000000000000000000000000000907fffffffffffff00000000000000000000000000000000000000000000000000006001600160c01b0319946031979487527f0200000000000000000000000000000000000000000000000000000000000000602088015260d01b16602186015260f01b16602784015260c01b1660298201520190565b51902093905b90615231565b60046040517fa9c58615000000000000000000000000000000000000000000000000000000008152fd5b6001036154ee57600f61540c82846149c5565b106153cf5761541b9083615cf6565b94906154e4615430825165ffffffffffff1690565b91610a1e61546c61545e6020936152fe858201976154586109598a516001600160401b031690565b90614fca565b94516001600160401b031690565b6040519485938401968791927fffffffffffff0000000000000000000000000000000000000000000000000000602f946001600160c01b03199385527f0100000000000000000000000000000000000000000000000000000000000000602086015260d01b16602184015260c01b1660278201520190565b51902093906153c9565b60046040517ffdbe1dce000000000000000000000000000000000000000000000000000000008152fd5b6004604051637ddbe9e960e11b8152fd5b6001600160a01b03809116805f52601360205265ffffffffffff908160405f2054169283156155585750505090565b80919293506011541682145f14615570575050505f90565b601254161461557c5790565b50600190565b6001600160801b0390818111615596571690565b608460405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152fd5b1561560757565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b61568b60ff60165460081c1661568681615600565b615600565b6001601755565b61570061569d610786565b5f815260016020820152606460408201526402540be4006060820152670fffffffffffffff6080820152620186a060a082015260015f526101f06020527f8ef17085e6277aed8f6f01c714d30dd99bcfb12fc3881d7c54391ffa2a994b0e614831565b5f80526101f160205261573b7fb90953adcbf35a931ac053a7d0fe43fdd42a03d188a6bab41f6e6ed74a0821d2805461ffff19166001179055565b6040517ff1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c81806157a7839490620186a060c060e0840193600181525f602082015260016040820152606460608201526402540be4006080820152670fffffffffffffff60a08201520152565b0390a1600460206157c3610f276006546001600160a01b031690565b60405192838092631f209df760e11b82525afa908115611379576158e3916001600160a01b03915f916158e8575b50166158686157fe610786565b6001600160a01b038316815260016020820152620f4240604082015260016060820152670fffffffffffffff6080820152620f424060a082015260035f526101f06020527fdcc7741fcb270a804667781042ff853c8745e471ecf5da3709c83259ac436883614831565b615895615887826001600160a01b03165f526101f160205260405f2090565b805461ffff19166003179055565b604051918291829190916001600160a01b0360e0820193600383521660208201526001604082015260c0620f42409182606082015260016080820152670fffffffffffffff60a08201520152565b0390a1565b615901915060203d6020116127e2576127d38183610745565b5f6157f1565b6159c2915f806001600160a01b03604051946159778661596960209a8b8301987fa9059cbb000000000000000000000000000000000000000000000000000000008a5260248401602090939291936001600160a01b0360408201951681520152565b03601f198101885287610745565b1692604051946159868661070e565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af16159bc614da7565b91615ef8565b805190828215928315615a45575b505050156159db5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b615a559350820181019101614408565b5f82816159d0565b93906fffffffffffffffffffffffffffffffff1992937fffff0000000000000000000000000000000000000000000000000000000000009160405195602087019788527fffffffffffff0000000000000000000000000000000000000000000000000000809260d01b16604088015260d01b16604686015260f01b16604c84015260801b16604e820152603e8152606081018181106001600160401b038211176107095760405251902090565b359060208110615b18575090565b5f199060200360031b1b1690565b5f9160a08106615b535760a081049160068311615c4f57905f915b838310615b645750505049615b535790565b6004604051630aa47a8360e11b8152fd5b909193615b8c615b7386614a5b565b615b84615b7f88614f75565b614a5b565b908585614e8a565b615ba1615b9b82849594614e6b565b90615b0a565b91615baf615b9b8386614e79565b918849938415615b5357615bc9615bee9260019787615dc3565b6040948551928391602096878401948591606093918352602083015260408201520190565b0391615c02601f1993848101835282610745565b5190209289615c1a575050509050945b019190615b41565b615c3a615c46939551948592830196879091604092825260208201520190565b03908101835282610745565b51902094615c12565b6040517fd8ae856600000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b6006820192918381116141d657600691615c9e8582511015615e62565b01015190565b60269190601e600891615c9e8582511015615e62565b6008820192918381116141d657600891615c9e8582511015615e62565b919060018101908181116141d657615cef919361428c565b5160f81c90565b6001600160401b039291615d3e65ffffffffffff60405193615d178561070e565b5f8552615d33615d2d60208701955f8752614f83565b82615c81565b929092168552615cba565b9490941690529190565b919060405190606082016001600160401b0394838210868311176107095765ffffffffffff916040525f845260208401905f8252615d8f615d2d60408701955f8752614f83565b93909316855260028301918284116141d65761ffff6002615d3e95615db78686511015615e62565b84010151169052615cba565b5f9192908291615df660408096838251948592602084019788528484013781018683820152036020810184520182610745565b5190600a5afa615e04614da7565b9015615e525781818051810103126104fc578101517f8c1258acd66282b7ccc627f7f65e27faac425bfd0001a40100000000ffffffff01615e425750565b600490516358c6239f60e11b8152fd5b600482516358c6239f60e11b8152fd5b15615e6957565b606460405162461bcd60e51b815260206004820152600160248201527f53000000000000000000000000000000000000000000000000000000000000006044820152fd5b15615eb457565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b91929015615f185750815115615f0c575090565b61499f903b1515615ead565b825190915015615f2b5750805190602001fd5b604460209160405192839162461bcd60e51b83528160048401528051918291826024860152018484015e5f828201840152601f01601f19168101030190fdfea26469706673582212204fc5262796ab368153795eb1d901315248b207f5b57e06a37b96ad813f57aea264736f6c63430008190033

Verified Source Code Full Match

Compiler: v0.8.25+commit.b61c2a91 EVM: cancun Optimization: Yes (1000 runs)
Initializable.sol 166 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}
ReentrancyGuardUpgradeable.sol 89 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
AddressUpgradeable.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
SafeCast.sol 1136 lines
// 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);
    }
}
AdditionalZkLighter.sol 662 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IEvents.sol";
import "./lib/TxTypes.sol";
import "./Storage.sol";
import "./ExtendableStorage.sol";

/// @title zkLighter Additional Contract
/// @notice zkLighter Contract delegates some of its functionality to this contract
/// @author zkLighter Team
contract AdditionalZkLighter is IEvents, Storage, ReentrancyGuardUpgradeable, ExtendableStorage {
  error AdditionalZkLighter_InvalidAssetIndex();
  error AdditionalZkLighter_InvalidDepositAmount();
  error AdditionalZkLighter_InvalidWithdrawAmount();
  error AdditionalZkLighter_InvalidAccountIndex();
  error AdditionalZkLighter_InvalidDepositBatchLength();
  error AdditionalZkLighter_InvalidApiKeyIndex();
  error AdditionalZkLighter_InvalidPubKey();
  error AdditionalZkLighter_RecipientAddressInvalid();
  error AdditionalZkLighter_InvalidMarketStatus();
  error AdditionalZkLighter_InvalidMarginMode();
  error AdditionalZkLighter_InvalidExtensionMultiplier();
  error AdditionalZkLighter_InvalidFeeAmount();
  error AdditionalZkLighter_InvalidMarginFraction();
  error AdditionalZkLighter_InvalidMinAmounts();
  error AdditionalZkLighter_InvalidConfigPeriod();
  error AdditionalZkLighter_InvalidMarketType();
  error AdditionalZkLighter_InvalidShareAmount();
  error AdditionalZkLighter_InvalidCreateOrderParameters();
  error AdditionalZkLighter_AccountIsNotRegistered();
  error AdditionalZkLighter_InvalidBatch();
  error AdditionalZkLighter_InvalidFundingClampsOrInterestRate();
  error AdditionalZkLighter_InvalidMarketLimits();
  error AdditionalZkLighter_StateRootUpgradeVerifierFailed();

  function updateStateRoot(
    StoredBatchInfo calldata _lastStoredBatch,
    bytes32 _stateRoot,
    bytes32 _validiumRoot,
    bytes calldata proof
  ) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(_lastStoredBatch)) {
      revert AdditionalZkLighter_InvalidBatch();
    }

    if (executedBatchesCount != committedBatchesCount) {
      revert AdditionalZkLighter_InvalidBatch();
    }

    if (stateRootUpgradeVerifier != IZkLighterStateRootUpgradeVerifier(address(0))) {
      bytes32 hashOutput = keccak256(abi.encodePacked(stateRoot, validiumRoot, _stateRoot, _validiumRoot));

      uint256[] memory inputs = new uint256[](1);
      inputs[0] = uint256(hashOutput) % BN254_MODULUS;

      bool success = stateRootUpgradeVerifier.Verify(proof, inputs);
      if (!success) {
        revert AdditionalZkLighter_StateRootUpgradeVerifierFailed();
      }

      stateRootUpgradeVerifier = IZkLighterStateRootUpgradeVerifier(address(0));
    } else {
      revert AdditionalZkLighter_StateRootUpgradeVerifierFailed();
    }

    stateRoot = _stateRoot;
    validiumRoot = _validiumRoot;
    lastVerifiedStateRoot = _stateRoot;
    lastVerifiedValidiumRoot = _validiumRoot;
    stateRootUpdates[committedBatchesCount] = _stateRoot;

    emit StateRootUpdate(committedBatchesCount, _lastStoredBatch.stateRoot, _stateRoot);
  }

  /// @notice Deposit ETH or ERC20 asset to zkLighter
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _amount ETH or ERC20 asset amount to deposit
  /// @param _to The receiver L1 address
  function _deposit(address[] memory _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256[] memory _amount) internal {
    AssetConfig memory assetConfig = assetConfigs[_assetIndex];
    if (_assetIndex != NATIVE_ASSET_INDEX) {
      if (assetConfig.tokenAddress == address(0)) {
        revert AdditionalZkLighter_InvalidAssetIndex();
      }
      if (msg.value != 0) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      }
    }
    uint256 minDeposit = uint256(assetConfig.minDepositTicks) * uint256(assetConfig.tickSize);
    uint256 depositCap = uint256(assetConfig.depositCapTicks) * uint256(assetConfig.tickSize);
    uint256 totalAmount = 0;
    for (uint256 i = 0; i < _amount.length; ++i) {
      totalAmount += _amount[i];
      if (_amount[i] < minDeposit || _amount[i] % assetConfig.tickSize != 0) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      }
      if (_to[i] == address(0)) {
        revert AdditionalZkLighter_RecipientAddressInvalid();
      }
    }

    uint256 balanceAfter = 0;
    if (_assetIndex == NATIVE_ASSET_INDEX) {
      balanceAfter = address(this).balance;
      if (msg.value != totalAmount) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      }
    } else {
      IERC20 _token = IERC20(assetConfig.tokenAddress);
      uint256 balanceBefore = _token.balanceOf(address(this));
      SafeERC20.safeTransferFrom(_token, msg.sender, address(this), totalAmount);
      balanceAfter = _token.balanceOf(address(this));
      if (balanceAfter < balanceBefore + totalAmount) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      } else if (balanceAfter > balanceBefore + totalAmount) {
        uint256 excessAmount = balanceAfter - (balanceBefore + totalAmount);
        uint64 baseExcessAmount = SafeCast.toUint64(excessAmount / assetConfig.tickSize);
        increaseBalanceToWithdraw(TREASURY_ACCOUNT_INDEX, _assetIndex, baseExcessAmount);
      }
    }

    if (balanceAfter > depositCap) {
      revert AdditionalZkLighter_InvalidDepositAmount();
    }
    for (uint256 i = 0; i < _amount.length; ++i) {
      uint64 baseAmount = SafeCast.toUint64(_amount[i] / assetConfig.tickSize);
      registerDeposit(_to[i], _assetIndex, _routeType, baseAmount);
    }
  }

  /// @notice Deposit asset to Lighter
  /// @param _to The receiver L1 address
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _amount asset amount to deposit
  function deposit(address _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256 _amount) external payable nonReentrant onlyActive {
    uint256[] memory amount = new uint256[](1);
    amount[0] = _amount;
    address[] memory to = new address[](1);
    to[0] = _to;
    _deposit(to, _assetIndex, _routeType, amount);
  }

  /// @notice Deposit USDC to Lighter for multiple users
  /// @param _amount Array of USDC Token amounts
  /// @param _to Array of receiver L1 addresses
  /// @param _accountIndex Array of account index values, will be used in the future
  function depositBatch(uint64[] calldata _amount, address[] calldata _to, uint48[] calldata _accountIndex) external nonReentrant onlyActive {
    if (_amount.length != _to.length || _amount.length != _accountIndex.length || _amount.length == 0 || _amount.length > MAX_BATCH_DEPOSIT_LENGTH) {
      revert AdditionalZkLighter_InvalidDepositBatchLength();
    }
    uint256[] memory amount = new uint256[](_amount.length);
    for (uint256 i = 0; i < _amount.length; ++i) {
      amount[i] = uint256(_amount[i]);
    }
    _deposit(_to, USDC_ASSET_INDEX, TxTypes.RouteType.Perps, amount);
  }

  /// @notice Change Lighter public key for an account api key slot
  function changePubKey(uint48 _accountIndex, uint8 _apiKeyIndex, bytes calldata _pubKey) external nonReentrant onlyActive {
    if (_accountIndex > MAX_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    if (_apiKeyIndex > MAX_API_KEY_INDEX) {
      revert AdditionalZkLighter_InvalidApiKeyIndex();
    }

    // Verify that the public key is of the correct length
    if (_pubKey.length != PUB_KEY_BYTES_SIZE) {
      revert AdditionalZkLighter_InvalidPubKey();
    }

    // Verify that the public key is not empty
    for (uint8 i = 0; i < _pubKey.length; ++i) {
      if (_pubKey[i] != 0) {
        break;
      }
      if (i == _pubKey.length - 1) {
        revert AdditionalZkLighter_InvalidPubKey();
      }
    }

    // Verify that the public key is in the field
    for (uint8 i = 0; i < 5; i++) {
      bytes memory elem = _pubKey[(8 * i):(8 * (i + 1))];
      uint64 elemValue = 0;
      for (uint8 j = 0; j < 8; j++) {
        elemValue = elemValue + (uint64(uint8(elem[j])) << (8 * j));
      }
      if (elemValue >= GOLDILOCKS_MODULUS) {
        revert AdditionalZkLighter_InvalidPubKey();
      }
    }

    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }

    // Add priority request to the queue
    TxTypes.ChangePubKey memory _tx = TxTypes.ChangePubKey({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      apiKeyIndex: _apiKeyIndex,
      pubKey: _pubKey
    });
    bytes memory pubData = TxTypes.writeChangePubKeyPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1ChangePubKey, pubData, pubData);
  }

  /// @notice Create new asset
  /// @param _params Config parameters
  function setSystemConfig(TxTypes.SetSystemConfig calldata _params) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);
    if (_params.liquidityPoolCooldownPeriod > MAX_CONFIG_PERIOD || _params.stakingPoolLockupPeriod > MAX_CONFIG_PERIOD) {
      revert AdditionalZkLighter_InvalidConfigPeriod();
    }
    if (_params.liquidityPoolIndex > NIL_ACCOUNT_INDEX || _params.stakingPoolIndex > NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    bytes memory priorityRequest = TxTypes.writeSetSystemConfigPubDataForPriorityQueue(_params);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1SetSystemConfig, priorityRequest, priorityRequest);
  }

  /// @notice Create new asset
  /// @param _l1Decimals [metadata] Number of decimals of the asset on L1
  /// @param _decimals [metadata] Number of decimals of the asset in Lighter
  /// @param _symbol [metadata] symbol of the asset, formatted as bytes32
  /// @param _params Asset parameters
  function registerAsset(
    uint8 _l1Decimals,
    uint8 _decimals,
    bytes32 _symbol,
    TxTypes.RegisterAsset calldata _params
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    AssetConfig memory config = assetConfigs[_params.assetIndex];
    if (_params.assetIndex != NATIVE_ASSET_INDEX && config.tokenAddress == address(0)) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
    if (_params.extensionMultiplier != config.extensionMultiplier) {
      revert AdditionalZkLighter_InvalidExtensionMultiplier();
    }
    if (_params.minL2TransferAmount == 0 || _params.minL2TransferAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.minL2WithdrawalAmount == 0 || _params.minL2WithdrawalAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.marginMode > uint8(type(TxTypes.AssetMarginMode).max)) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Enabled) && _params.assetIndex != USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Disabled) && _params.assetIndex == USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    bytes memory priorityRequest = TxTypes.writeRegisterAssetPubDataForPriorityQueue(_params);
    bytes memory metadata = TxTypes.writeRegisterAssetPubDataForPriorityQueueWithMetadata(
      priorityRequest,
      _l1Decimals,
      _decimals,
      config.tickSize,
      config.tokenAddress,
      _symbol
    );
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1RegisterAsset, priorityRequest, metadata);
    emit RegisterAsset(_params, _l1Decimals, _decimals, _symbol);
  }

  /// @notice Update asset parameters
  /// @param _params Asset update parameters
  function updateAsset(TxTypes.UpdateAsset calldata _params) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    validateAssetIndex(_params.assetIndex);
    if (_params.minL2TransferAmount == 0 || _params.minL2TransferAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.minL2WithdrawalAmount == 0 || _params.minL2WithdrawalAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.marginMode > uint8(type(TxTypes.AssetMarginMode).max)) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Enabled) && _params.assetIndex != USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Disabled) && _params.assetIndex == USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    bytes memory pubData = TxTypes.writeUpdateAssetPubDataForPriorityQueue(_params);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1UpdateAsset, pubData, pubData);
    emit UpdateAsset(_params);
  }

  /// @notice Create new market and an order book
  /// @param _size_decimals [metadata] Number of decimals to represent size of an order in the order book
  /// @param _price_decimals [metadata] Number of decimals to represent price of an order in the order book
  /// @param _symbol [metadata] symbol of the market, formatted as bytes32
  /// @param _params Market parameters
  function createMarket(
    uint8 _size_decimals,
    uint8 _price_decimals,
    bytes32 _symbol,
    TxTypes.CreateMarket calldata _params
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    validateCreateMarketParams(_params);

    // Add priority request to the queue
    bytes memory priorityRequest = TxTypes.writeCreateMarketPubDataForPriorityQueue(_params);
    bytes memory metadata = TxTypes.writeCreateMarketPubDataForPriorityQueueWithMetadata(priorityRequest, _size_decimals, _price_decimals, _symbol);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1CreateMarket, priorityRequest, metadata);
    emit CreateMarket(_params, _size_decimals, _price_decimals, _symbol);
  }

  function validateCommonPerpMarketParams(TxTypes.CommonPerpsData memory perpParams) internal pure {
    if (perpParams.makerFee > FEE_TICK || perpParams.takerFee > FEE_TICK || perpParams.liquidationFee > FEE_TICK) {
      revert AdditionalZkLighter_InvalidFeeAmount();
    }
    if (
      perpParams.closeOutMarginFraction == 0 ||
      perpParams.closeOutMarginFraction > perpParams.maintenanceMarginFraction ||
      perpParams.maintenanceMarginFraction > perpParams.minInitialMarginFraction ||
      perpParams.minInitialMarginFraction > perpParams.defaultInitialMarginFraction ||
      perpParams.defaultInitialMarginFraction > MARGIN_TICK
    ) {
      revert AdditionalZkLighter_InvalidMarginFraction();
    }
    if (perpParams.interestRate > FUNDING_RATE_TICK) {
      revert AdditionalZkLighter_InvalidFundingClampsOrInterestRate();
    }
    if (perpParams.fundingClampSmall > FUNDING_RATE_TICK || perpParams.fundingClampBig > FUNDING_RATE_TICK) {
      revert AdditionalZkLighter_InvalidFundingClampsOrInterestRate();
    }
    if (
      perpParams.minBaseAmount == 0 ||
      perpParams.minBaseAmount > MAX_ORDER_BASE_AMOUNT ||
      perpParams.minQuoteAmount == 0 ||
      perpParams.minQuoteAmount > MAX_ORDER_QUOTE_AMOUNT
    ) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (perpParams.orderQuoteLimit > MAX_ORDER_QUOTE_AMOUNT || perpParams.minQuoteAmount > perpParams.orderQuoteLimit) {
      revert AdditionalZkLighter_InvalidMarketLimits();
    }
    if (perpParams.openInterestLimit < perpParams.orderQuoteLimit || perpParams.openInterestLimit > MAX_MARKET_OPEN_INTEREST) {
      revert AdditionalZkLighter_InvalidMarketLimits();
    }
  }

  function validateCommonSpotMarketParams(TxTypes.CommonSpotData memory spotParams) internal pure {
    if (spotParams.makerFee > FEE_TICK || spotParams.takerFee > FEE_TICK) {
      revert AdditionalZkLighter_InvalidFeeAmount();
    }
    if (
      spotParams.minBaseAmount == 0 ||
      spotParams.minBaseAmount > MAX_ORDER_BASE_AMOUNT ||
      spotParams.minQuoteAmount == 0 ||
      spotParams.minQuoteAmount > MAX_ORDER_QUOTE_AMOUNT
    ) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (spotParams.orderQuoteLimit > MAX_ORDER_QUOTE_AMOUNT || spotParams.minQuoteAmount > spotParams.orderQuoteLimit) {
      revert AdditionalZkLighter_InvalidMarketLimits();
    }
  }

  function validateCreateMarketParams(TxTypes.CreateMarket calldata _params) internal view {
    if (_params.marketType == TxTypes.MarketType.Perps) {
      TxTypes.CreateMarketPerpsData memory perpParams = TxTypes.readCreateMarketPerpsData(_params.marketData);
      if (_params.marketIndex > MAX_PERPS_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      if (perpParams.quoteMultiplier == 0 || perpParams.quoteMultiplier > MAX_QUOTE_MULTIPLIER) {
        revert AdditionalZkLighter_InvalidExtensionMultiplier();
      }
      validateCommonPerpMarketParams(perpParams.common);
    } else if (_params.marketType == TxTypes.MarketType.Spot) {
      TxTypes.CreateMarketSpotData memory spotParams = TxTypes.readCreateMarketSpotData(_params.marketData);
      if (_params.marketIndex < MIN_SPOT_MARKET_INDEX || _params.marketIndex > MAX_SPOT_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      if (spotParams.baseAssetIndex == spotParams.quoteAssetIndex) {
        revert AdditionalZkLighter_InvalidAssetIndex();
      }
      validateAssetIndex(spotParams.baseAssetIndex);
      validateAssetIndex(spotParams.quoteAssetIndex);
      if (
        spotParams.sizeExtensionMultiplier == 0 ||
        spotParams.sizeExtensionMultiplier > MAX_ASSET_EXTENSION_MULTIPLIER ||
        spotParams.sizeExtensionMultiplier % Config.FEE_TICK != 0
      ) {
        revert AdditionalZkLighter_InvalidExtensionMultiplier();
      }
      if (
        spotParams.quoteExtensionMultiplier == 0 ||
        spotParams.quoteExtensionMultiplier > MAX_ASSET_EXTENSION_MULTIPLIER ||
        spotParams.quoteExtensionMultiplier % Config.FEE_TICK != 0
      ) {
        revert AdditionalZkLighter_InvalidExtensionMultiplier();
      }
      validateCommonSpotMarketParams(spotParams.common);
    } else {
      revert AdditionalZkLighter_InvalidMarketType();
    }
  }

  /// @notice Update order book status
  /// @param _params Order book update parameters
  function updateMarket(TxTypes.UpdateMarket calldata _params) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);
    validateUpdateMarketParams(_params);
    // Add priority request to the queue
    bytes memory pubdata = TxTypes.writeUpdateMarketPubDataForPriorityQueue(_params);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1UpdateMarket, pubdata, pubdata);
    emit UpdateMarket(_params);
  }

  function validateUpdateMarketParams(TxTypes.UpdateMarket calldata _params) internal pure {
    if (_params.marketType == TxTypes.MarketType.Perps) {
      if (_params.marketIndex > MAX_PERPS_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      TxTypes.UpdateMarketPerps memory perpParams = TxTypes.readUpdateMarketPerpsData(_params.marketData);
      if (perpParams.status != uint8(MarketStatus.ACTIVE) && perpParams.status != uint8(MarketStatus.NONE)) {
        revert AdditionalZkLighter_InvalidMarketStatus();
      }
      validateCommonPerpMarketParams(perpParams.common);
    } else if (_params.marketType == TxTypes.MarketType.Spot) {
      if (_params.marketIndex < MIN_SPOT_MARKET_INDEX || _params.marketIndex > MAX_SPOT_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      TxTypes.UpdateMarketSpot memory spotParams = TxTypes.readUpdateMarketSpotData(_params.marketData);
      if (spotParams.status != uint8(MarketStatus.ACTIVE) && spotParams.status != uint8(MarketStatus.NONE)) {
        revert AdditionalZkLighter_InvalidMarketStatus();
      }
      validateCommonSpotMarketParams(spotParams.common);
    } else {
      revert AdditionalZkLighter_InvalidMarketType();
    }
  }

  /// @notice Cancels all orders
  function cancelAllOrders(uint48 _accountIndex) external nonReentrant onlyActive {
    if (_accountIndex > MAX_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }

    // Add priority request to the queue
    TxTypes.CancelAllOrders memory _tx = TxTypes.CancelAllOrders({accountIndex: _accountIndex, masterAccountIndex: _masterAccountIndex});
    bytes memory pubData = TxTypes.writeCancelAllOrdersPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1CancelAllOrders, pubData, pubData);
  }

  /// @notice Withdraw ETH or ERC20 from zkLighter
  /// @param _assetIndex Asset index, 0 for ETH
  /// @param _routeType Route type
  /// @param _accountIndex Account index to withdraw from
  /// @param _baseAmount Amount of base token to withdraw, in ticks
  function withdraw(uint48 _accountIndex, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) external nonReentrant onlyActive {
    AssetConfig memory assetConfig = assetConfigs[_assetIndex];
    if (_assetIndex != NATIVE_ASSET_INDEX && assetConfig.tokenAddress == address(0)) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
    if (assetConfig.withdrawalsEnabled == 0) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
    uint256 depositCapTicks = assetConfig.depositCapTicks;
    if (_baseAmount == 0 || _baseAmount > depositCapTicks) {
      revert AdditionalZkLighter_InvalidWithdrawAmount();
    }
    if (_routeType != TxTypes.RouteType.Perps && _routeType != TxTypes.RouteType.Spot) {
      revert AdditionalZkLighter_InvalidWithdrawAmount();
    }
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }

    TxTypes.L1Withdraw memory _tx = TxTypes.L1Withdraw({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      assetIndex: _assetIndex,
      routeType: _routeType,
      baseAmount: _baseAmount
    });

    bytes memory pubData = TxTypes.writeWithdrawPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1Withdraw, pubData, pubData);
  }

  /// @notice Create an order for a Lighter account
  /// @param _accountIndex Account index
  /// @param _marketIndex Market index
  /// @param _baseAmount Amount of base token
  /// @param _price Price of the order
  /// @param _isAsk Flag to indicate if the order is ask or bid
  /// @param _orderType Order type
  function createOrder(
    uint48 _accountIndex,
    uint16 _marketIndex,
    uint48 _baseAmount,
    uint32 _price,
    uint8 _isAsk,
    uint8 _orderType
  ) external nonReentrant onlyActive {
    if (_accountIndex > MAX_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }
    if (_isAsk != 0 && _isAsk != 1) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_orderType != uint8(TxTypes.OrderType.LimitOrder) && _orderType != uint8(TxTypes.OrderType.MarketOrder)) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_baseAmount != NIL_ORDER_BASE_AMOUNT && (_baseAmount > MAX_ORDER_BASE_AMOUNT || _baseAmount < MIN_ORDER_BASE_AMOUNT)) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_price > MAX_ORDER_PRICE || _price < MIN_ORDER_PRICE) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_marketIndex > MAX_PERPS_MARKET_INDEX) {
      revert AdditionalZkLighter_InvalidMarketType();
    }

    TxTypes.CreateOrder memory _tx = TxTypes.CreateOrder({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      marketIndex: _marketIndex,
      baseAmount: _baseAmount,
      price: _price,
      isAsk: _isAsk,
      orderType: _orderType
    });

    bytes memory pubData = TxTypes.writeCreateOrderPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1CreateOrder, pubData, pubData);
  }

  /// @notice Burn shares of an account in a public pool
  /// @param _accountIndex Account index
  /// @param _publicPoolIndex Public pool index
  /// @param _shareAmount Amount of shares to burn
  function burnShares(uint48 _accountIndex, uint48 _publicPoolIndex, uint64 _shareAmount) external nonReentrant onlyActive {
    validatePoolExit(_accountIndex, _publicPoolIndex);
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }
    if (_shareAmount < MIN_POOL_SHARES_TO_MINT_OR_BURN || _shareAmount > MAX_POOL_SHARES_TO_MINT_OR_BURN) {
      revert AdditionalZkLighter_InvalidShareAmount();
    }

    TxTypes.BurnShares memory _tx = TxTypes.BurnShares({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      publicPoolIndex: _publicPoolIndex,
      sharesAmount: _shareAmount
    });
    bytes memory pubData = TxTypes.writeBurnSharesPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1BurnShares, pubData, pubData);
  }

  /// @notice Register deposit request - pack pubdata, add into onchainOpsCheck and emit OnchainDeposit event
  /// @param _toAddress Receiver Account's L1 address
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _baseAmount Asset amount
  function registerDeposit(address _toAddress, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) internal {
    uint48 _toAccountIndex = getAccountIndexFromAddress(_toAddress);
    // No account could be found for the address
    if (_toAccountIndex <= MAX_SYSTEM_ACCOUNT_INDEX) {
      _toAddress = address(0);
    } else if (_toAccountIndex == NIL_ACCOUNT_INDEX) {
      ++lastAccountIndex;
      _toAccountIndex = lastAccountIndex;
      if (_toAccountIndex > MAX_MASTER_ACCOUNT_INDEX) {
        revert AdditionalZkLighter_InvalidAccountIndex();
      }
      addressToAccountIndex[_toAddress] = _toAccountIndex;
    }
    TxTypes.Deposit memory _tx = TxTypes.Deposit({
      accountIndex: _toAccountIndex,
      toAddress: _toAddress,
      assetIndex: _assetIndex,
      routeType: _routeType,
      baseAmount: _baseAmount
    });
    bytes memory pubData = TxTypes.writeDepositPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1Deposit, pubData, pubData);
    emit Deposit(_toAccountIndex, _toAddress, _assetIndex, _routeType, _baseAmount);
  }

  /// @notice Saves priority request in storage
  /// @dev Calculates expiration timestamp of the request and stores the request in priorityRequests
  /// @param _pubdataType Priority request public data type
  /// @param _priorityRequest Request public data that is hashed and stored in priorityRequests
  /// @param _pubDataWithMetadata Request public data that is emitted in NewPriorityRequest event including the metadata
  function addPriorityRequest(uint8 _pubdataType, bytes memory _priorityRequest, bytes memory _pubDataWithMetadata) internal {
    // Expiration timestamp is current block number + priority expiration delta
    uint64 expirationTimestamp = SafeCast.toUint64(block.timestamp + PRIORITY_EXPIRATION);
    uint64 nextPriorityRequestId = executedPriorityRequestCount + openPriorityRequestCount;
    bytes32 pubDataPrefix = bytes32(0);
    if (nextPriorityRequestId > 0) {
      pubDataPrefix = priorityRequests[nextPriorityRequestId - 1].prefixHash;
    }
    bytes memory paddedPubData = new bytes(MAX_PRIORITY_REQUEST_PUBDATA_SIZE);
    for (uint256 i = 0; i < _priorityRequest.length; ++i) {
      paddedPubData[i] = _priorityRequest[i];
    }
    priorityRequests[nextPriorityRequestId] = PriorityRequest({
      prefixHash: keccak256(abi.encodePacked(pubDataPrefix, paddedPubData)),
      expirationTimestamp: expirationTimestamp
    });
    emit NewPriorityRequest(msg.sender, nextPriorityRequestId, _pubdataType, _pubDataWithMetadata, expirationTimestamp);
    ++openPriorityRequestCount;
  }

  function increaseBalanceToWithdraw(uint48 _masterAccountIndex, uint16 _assetIndex, uint128 _baseAmount) internal {
    uint128 baseBalance = pendingAssetBalances[_assetIndex][_masterAccountIndex].balanceToWithdraw;
    pendingAssetBalances[_assetIndex][_masterAccountIndex] = PendingBalance(baseBalance + _baseAmount, FILLED_GAS_RESERVE_VALUE);
  }

  function validateAssetIndex(uint16 _assetIndex) internal view {
    if (_assetIndex != NATIVE_ASSET_INDEX && assetConfigs[_assetIndex].tokenAddress == address(0)) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
  }

  function validatePoolExit(uint48 _accountIndex, uint48 _poolIndex) internal pure {
    if (
      _accountIndex > MAX_ACCOUNT_INDEX || _accountIndex == _poolIndex || _poolIndex > MAX_ACCOUNT_INDEX || _poolIndex <= MAX_MASTER_ACCOUNT_INDEX
    ) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
  }
}
Config.sol 147 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter Configuration Contract
/// @author zkLighter Team
contract Config {
  /// @dev Max master account id that could be registered in the
  /// network (excluding treasury, which is set as accountIndex = 0)
  /// Sub accounts and pool indexes start from 2**47 to 2**48 - 2 and are set by the sequencer
  uint48 public constant MAX_MASTER_ACCOUNT_INDEX = 2 ** 47 - 1;

  /// @dev Max account id that could be registered in the network
  uint48 public constant MAX_ACCOUNT_INDEX = 2 ** 48 - 2;

  /// @dev Nil account id, that represents an empty account
  uint48 public constant NIL_ACCOUNT_INDEX = 2 ** 48 - 1;

  /// @dev Max API key index that could be registered for an account
  uint8 public constant MAX_API_KEY_INDEX = 254; // 2 ** 8 - 2

  /// @dev Min asset index that could be registered in the exchange
  uint16 public constant MIN_ASSET_INDEX = 1;

  /// @dev Native asset index, that represents the base chain native asset (ETH)
  uint16 public constant NATIVE_ASSET_INDEX = 1;

  /// @dev USDC asset index
  uint16 public constant USDC_ASSET_INDEX = 3;

  /// @dev Max asset index that could be registered in the exchange (to be extended in the future)
  uint16 public constant MAX_ASSET_INDEX = 62; // 2 ** 6 - 2

  /// @dev Max tick size for an asset
  uint128 public constant MAX_TICK_SIZE = 2 ** 128 - 1;

  /// @dev Max deposit cap ticks for an asset
  uint64 public constant MAX_DEPOSIT_CAP_TICKS = 2 ** 60 - 1;

  /// @dev Max perps market index that could be registered to the exchange
  uint16 public constant MAX_PERPS_MARKET_INDEX = 254; // 2 ** 8 - 2

  /// @dev Min spot market index that could be registered to the exchange
  uint16 public constant MIN_SPOT_MARKET_INDEX = 2048; // 2 ** 11

  /// @dev Max spot market index that could be registered to the exchange
  uint16 public constant MAX_SPOT_MARKET_INDEX = 4094; // 2 ** 12 - 2

  /// @dev Max price an order can have
  uint32 public constant MAX_ORDER_PRICE = 2 ** 32 - 1;

  /// @dev Min price an order can have
  uint32 public constant MIN_ORDER_PRICE = 1;

  /// @dev Nil order base amount
  uint48 public constant NIL_ORDER_BASE_AMOUNT = 0;

  /// @dev Max order base amount
  uint48 public constant MAX_ORDER_BASE_AMOUNT = 2 ** 48 - 1;

  /// @dev Max order quote amount
  uint48 public constant MAX_ORDER_QUOTE_AMOUNT = 2 ** 48 - 1;

  /// @dev Min order base amount
  uint48 public constant MIN_ORDER_BASE_AMOUNT = 1;

  /// @dev Max amount of pool shares that can be minted or burned
  uint64 public constant MAX_POOL_SHARES_TO_MINT_OR_BURN = 2 ** 60 - 1;

  /// @dev Min amount of pool shares that can be minted or burned
  uint64 public constant MIN_POOL_SHARES_TO_MINT_OR_BURN = 1;

  /// @dev Max amount of staking shares that can be minted or burned
  uint64 public constant MAX_STAKING_SHARES_TO_MINT_OR_BURN = 2 ** 60 - 1;

  /// @dev Min amount of staking shares that can be minted or burned
  uint64 public constant MIN_STAKING_SHARES_TO_MINT_OR_BURN = 1;

  /// @dev Expiration timestamp delta for priority request
  /// @dev Priority expiration timestamp should be greater than the operation execution timestamp
  uint256 public constant PRIORITY_EXPIRATION = 14 days;

  /// @dev Margin tick to transform margin values in form x * 0.01%
  uint16 constant MARGIN_TICK = 10_000;

  /// @dev Funding rate tick to transform funding values in form x * 0.0001%
  uint32 constant FUNDING_RATE_TICK = 1_000_000;

  /// @dev Fee tick to transform fee values in form x * 0.0001%
  uint32 constant FEE_TICK = 1_000_000;

  /// @dev Max value for quote multiplier
  uint32 constant MAX_QUOTE_MULTIPLIER = 10_000;

  /// @dev Max value for asset extension multiplier
  uint56 constant MAX_ASSET_EXTENSION_MULTIPLIER = 2 ** 56 - 1;

  /// @dev Max value for asset extended deposit cap ticks
  uint128 constant MAX_EXTENDED_DEPOSIT_CAP_TICKS = 2 ** 80 - 1;

  /// @dev Size of the public key for a Lighter API key
  uint8 constant PUB_KEY_BYTES_SIZE = 40;

  /// @dev Address of the blob point evaluation precompile (EIP-4844)
  address constant POINT_EVALUATION_PRECOMPILE_ADDRESS = address(0x0A);

  /// @dev Max priority request pubdata size stat is written to the priority request queue
  uint256 constant MAX_PRIORITY_REQUEST_PUBDATA_SIZE = 100;

  /// @dev BLS Modulus value defined in EIP-4844,
  /// returned by the precompile if successfully evaluated
  uint256 constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;

  /// @dev Scalar field of bn254
  uint256 constant BN254_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

  /// @dev Evaluation point x (32 bytes) || evaluation point y (32 bytes) ||
  /// commitment (48 bytes) || proof (48 bytes)) = 160 bytes
  uint256 constant BLOB_DATA_COMMITMENT_BYTE_SIZE = 160;

  /// @dev Goldilocks prime field modulus, 2^64 - 2^32 + 1
  uint64 constant GOLDILOCKS_MODULUS = 0xffffffff00000001;

  /// @dev Max open interest per market
  uint64 constant MAX_MARKET_OPEN_INTEREST = (2 ** 56) - 1;

  /// @dev Max batch deposit length
  uint64 public constant MAX_BATCH_DEPOSIT_LENGTH = 1000;

  /// @dev Max # of blobs a batch can have
  uint256 constant MAX_BLOB_COUNT = 6;

  /// @dev Treasury account index (system account)
  uint48 constant TREASURY_ACCOUNT_INDEX = 0;

  /// @dev Insurance fund operator account index (system account)
  uint48 constant INSURANCE_FUND_OPERATOR_ACCOUNT_INDEX = 1; // Account index for the insurance fund operator account

  /// @dev Max system account index, 2 is left empty for future use
  uint48 constant MAX_SYSTEM_ACCOUNT_INDEX = 2;

  function _hasCode(address account) internal view returns (bool) {
    return account.code.length > 0;
  }

  uint48 constant MAX_CONFIG_PERIOD = 1000 * 60 * 60 * 24 * 14; // 14 days in milliseconds
}
ExtendableStorage.sol 39 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "./interfaces/IZkLighterStateRootUpgradeVerifier.sol";

/// @title zkLighter Extendable Storage Contract
/// @author zkLighter Team
contract ExtendableStorage {
  uint256[420] private __gap;

  /// @dev Stores new state root at the batch number if state root upgrade happened
  mapping(uint64 => bytes32) public stateRootUpdates;

  /// @dev Verifier contract, used for verifying state root upgrade proofs
  IZkLighterStateRootUpgradeVerifier internal stateRootUpgradeVerifier;

  /// @dev Stores if the desert mode was performed for the account index
  /// @dev Deprecated: use accountPerformedDesertForAsset instead
  mapping(uint48 => bool) internal DEPRECATED_accountPerformedDesert;

  struct PendingBalance {
    uint128 balanceToWithdraw;
    uint8 gasReserveValue;
  }

  struct AssetConfig {
    address tokenAddress; // Base layer token address
    uint8 withdrawalsEnabled; // 0 if disabled, 1 if enabled
    uint56 extensionMultiplier; // Internal asset extension multiplier
    uint128 tickSize; // Balance change unit before applying the extension multiplier
    uint64 depositCapTicks; // Max deposit cap in ticks
    uint64 minDepositTicks; // Min deposit in ticks
  }

  mapping(uint16 => AssetConfig) public assetConfigs; // id -> config
  mapping(address => uint16) public tokenToAssetIndex; // token -> id
  mapping(uint16 => mapping(uint48 => PendingBalance)) internal pendingAssetBalances;
  mapping(uint16 => mapping(uint48 => bool)) internal accountPerformedDesertForAsset;
}
IDesertVerifier.sol 13 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter DesertVerifier Interface
/// @author zkLighter Team
interface IDesertVerifier {
  /// @notice Verifies a SNARK proof for the desert mode
  /// @param proof The SNARK proof to verify
  /// @param public_inputs The public inputs for the proof
  /// @return success True if the proof is valid, false otherwise
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) external view returns (bool success);
}
IEvents.sol 67 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../lib/TxTypes.sol";

/// @title zkLighter Events Interface
/// @author zkLighter Team
interface IEvents {
  /// @notice Event emitted when a batch is committed
  event BatchCommit(uint64 batchNumber, uint32 batchSize, uint64 endBlockNumber);

  /// @notice Event emitted when a batch is verified
  event BatchVerification(uint64 batchNumber, uint32 batchSize, uint64 endBlockNumber);

  /// @notice Event emitted when batches until given batch number are executed
  event BatchesExecuted(uint64 batchNumber, uint64 endBlockNumber);

  /// @notice Event emitted when batches are reverted
  event BatchesRevert(uint64 newTotalBlocksCommitted);

  /// @notice Event emitted when user funds are deposited to a zkLighter account
  event Deposit(uint48 toAccountIndex, address toAddress, uint16 assetIndex, TxTypes.RouteType routeType, uint128 baseAmount);

  /// @notice Market created event
  event CreateMarket(TxTypes.CreateMarket params, uint8 sizeDecimals, uint8 priceDecimals, bytes32 symbol);

  /// @notice Asset config registered event
  event RegisterAssetConfig(
    uint16 assetIndex,
    address tokenAddress,
    uint8 withdrawalsEnabled,
    uint56 extensionMultiplier,
    uint128 tickSize,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  );

  /// @notice Asset config updated event
  event UpdateAssetConfig(uint16 assetIndex, uint8 withdrawalsEnabled, uint64 depositCapTicks, uint64 minDepositTicks);

  /// @notice Asset registered event
  event RegisterAsset(TxTypes.RegisterAsset params, uint8 l1Decimals, uint8 decimals, bytes32 symbol);

  /// @notice Asset updated event
  event UpdateAsset(TxTypes.UpdateAsset params);

  /// @notice Market updated event
  event UpdateMarket(TxTypes.UpdateMarket params);

  /// @notice Event emitted when user funds are withdrawn from contract
  event WithdrawPending(address indexed owner, uint16 assetIndex, uint128 baseAmount);

  /// @notice New priority request event. Emitted when a request is placed into mapping
  event NewPriorityRequest(address sender, uint64 serialId, uint8 pubdataType, bytes pubData, uint64 expirationTimestamp);

  /// @notice Desert mode entered event
  event DesertMode();

  /// @notice The treasury address changed
  event TreasuryUpdate(address newTreasury);

  /// @notice The insurance fund operator address changed
  event InsuranceFundOperatorUpdate(address newInsuranceFundOperator);

  /// @notice The state root upgrade event
  event StateRootUpdate(uint64 batchNumber, bytes32 oldStateRoot, bytes32 newStateRoot);
}
IGovernance.sol 50 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title zkLighter Events Interface
/// @author zkLighter Team
interface IGovernance {
  /// @notice Governor changed
  event NewGovernor(address newGovernor);

  /// @notice Validator status changed
  event ValidatorStatusUpdate(address validatorAddress, bool isActive);

  /// @notice Thrown in constructor when USDC is not a contract or zero address
  error ZkLighter_Governance_InvalidUSDCAddress();

  /// @notice Thrown in constructor when Governor Address is zero
  error ZkLighter_Governance_GovernorCannotBeZero();

  ///@notice Thrown by requireGovernor function and when the address is not a governor
  error ZkLighter_Governance_OnlyGovernor();

  /// @notice Thrown when the validator address is zero
  error ZkLighter_Governance_ValidatorCannotBeZero();

  /// @notice Thrown when the validator address is invalid
  error ZkLighter_Governance_InvalidValidator();

  /// @notice Change current governor
  /// @param _newGovernor Address of the new governor
  function changeGovernor(address _newGovernor) external;

  /// @return The address of the USDC address
  function usdc() external view returns (IERC20);

  /// @notice Check if specified address is governor
  /// @param _address Address to check
  function requireGovernor(address _address) external view;

  /// @notice Set validator address
  /// @param _validator Address of the validator
  /// @param _active Validator status
  function setValidator(address _validator, bool _active) external;

  /// @notice Check if specified address is validator
  /// @param _address Address to check
  function isActiveValidator(address _address) external view;
}
IZkLighter.sol 355 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "../Storage.sol";
import "./IEvents.sol";
import "../lib/TxTypes.sol";

/// @title zkLighter Interface
/// @author zkLighter Team
interface IZkLighter is IEvents {
  enum PubDataMode {
    Blob,
    Calldata
  }

  struct CommitBatchInfo {
    uint64 endBlockNumber;
    uint32 batchSize;
    uint64 startTimestamp;
    uint64 endTimestamp;
    uint32 priorityRequestCount;
    bytes32 prefixPriorityRequestHash;
    bytes32 onChainOperationsHash;
    bytes32 newStateRoot;
    bytes32 newValidiumRoot;
    bytes pubdataCommitments;
  }

  /// @notice Thrown when given commit batch data is inconsistent with the last stored batch
  error ZkLighter_InvalidPubDataMode();

  /// @notice Thrown when given commit batch data is inconsistent with the last stored batch
  error ZkLighter_NonIncreasingBlockNumber();

  /// @notice Thrown when given commit batch size is wrong
  error ZkLighter_InvalidBatchSize();

  /// @notice Thrown when given commit batch data is inconsistent with the last stored batch
  error ZkLighter_NonIncreasingTimestamp();

  /// @notice Thrown when given StoredBatchInfo hash doesn't match what is stored
  error ZkLighter_StoredBatchInfoMismatch();

  /// @notice Thrown when given priority request count is inconsistent with queued priority requests
  error ZkLighter_CommitBatchPriorityRequestCountMismatch();

  /// @notice Thrown when given priority request prefix hash doesn't match
  error ZkLighter_PriorityRequestPrefixHashMismatch();

  /// @notice Thrown when execute batches is called with different lengths of data
  error ZkLighter_ExecuteInputLengthMismatch();

  /// @notice Thrown when execute batches is called with input length greater than pending count
  error ZkLighter_ExecuteInputLengthGreaterThanPendingCount();

  /// @notice Thrown when revert batches is called on an already executed batch
  error ZkLighter_CannotRevertExecutedBatch();

  /// @notice Thrown when revert batches is called on the genesis batch
  error ZkLighter_CannotRevertGenesisBatch();

  /// @notice Thrown when given withdraw pubdata for a batch has invalid length
  error ZkLighter_InvalidPubDataLength();

  /// @notice Thrown when given withdraw pubdata for a batch has invalid data type
  error ZkLighter_InvalidPubDataType();

  /// @notice Thrown when given withdraw pubdata for a batch is invalid
  error ZkLighter_OnChainOperationsHashMismatch();

  /// @notice Thrown when implementation contract calls the initialise function on self
  error ZkLighter_CannotBeInitialisedByImpl();

  /// @notice Thrown when the initialisation parameters are invalid
  error ZkLighter_InvalidInitializeParameters();

  /// @notice Thrown when the upgrade parameters are invalid
  error ZkLighter_InvalidUpgradeParameters();

  /// @notice Thrown when requested amount is greater than the pending balance
  error ZkLighter_InvalidWithdrawAmount();

  /// @notice Thrown when the transferred amount is not a multiple of the tick size
  error ZkLighter_TransferredAmountNotMultipleOfTickSize();

  /// @notice Thrown when upgrade address(this) is the implementation
  error ZkLighter_OnlyProxyCanCallUpgrade();

  /// @notice Thrown when a restricted function which can be called only from zkLighterProxy is called by other address
  error ZkLighter_OnlyZkLighter();

  /// @notice thrown when ETH transfer fails
  error ZkLighter_ETHTransferFailed();

  /// @notice Thrown when rollup balance difference (before and after transfer) is bigger than `_maxAmount`
  error ZkLighter_RollUpBalanceBiggerThanMaxAmount();

  /// @notice Thrown when verifyBatch is called on a batch which is not yet committed
  error ZkLighter_CannotVerifyNonCommittedBatch();

  /// @notice Thrown when verifyBatch is called for invalid batch
  error ZkLighter_VerifyBatchNotInOrder();

  /// @notice Thrown when verifyBatch is called with invalid proof
  error ZkLighter_VerifyBatchProofFailed();

  /// @notice Thrown when given batch is not yet verified
  error ZkLighter_CannotExecuteNonVerifiedBatch();

  /// @notice Thrown when given batch either doesn't contain on chain operations or the order is wrong
  error ZkLighter_BatchNotInOnChainQueue();

  /// @notice ZkLighterImplementation cannot delegate to AdditionalZkLigher
  error ZkLighter_ImplCantDelegateToAddl();

  /// @notice Thrown when the new treasury address is zero
  error ZkLighter_TreasuryCannotBeZero();

  /// @notice Thrown when the new treasury address is already in use
  error ZkLighter_TreasuryCannotBeInUse();

  /// @notice Thrown when the new insurance fund operator address is zero
  error ZkLighter_InsuranceFundOperatorCannotBeZero();

  /// @notice Thrown when the new insurance fund operator address is already in use
  error ZkLighter_InsuranceFundOperatorCannotBeInUse();

  /// @notice Thrown when the point evaluation parameters are invalid
  error ZkLighter_InvalidPointEvaluationParams();

  /// @notice Thrown when the blob commitment parameters are invalid
  error ZkLighter_InvalidBlobCommitmentParams();

  error ZkLighter_InvalidAssetIndex();

  error ZkLighter_InvalidAssetConfigParams();

  error ZkLighter_DesertModeInactive();

  error ZkLighter_DesertVerifyProofFailed();

  error ZkLighter_AccountAlreadyPerformedDesertForAsset();

  error ZkLighter_NoOutstandingDepositsForCancelation();

  error ZkLighter_InvalidParamsForCancelOutstandingDeposits();

  error ZkLighter_DepositPubdataHashMismatch();

  /// @notice Thrown when the number of blobs in a batch exceeds the maximum allowed
  error ZkLighter_InvalidBlobCount(uint256);

  /// @notice Checks if Desert mode must be entered. If true - enters desert mode and emits DesertMode event
  /// @dev Desert mode must be entered in case of current L1 block timestamp is higher than the oldest priority request expiration timestamp
  /// @return bool Flag that is true if the desert mode must be entered
  function activateDesertMode() external returns (bool);

  /// @notice Performs the Desert Exit, can be called only when desertMode is active
  /// @param _accountIndex Account index of the user who is performing the desert exit
  /// @param _masterAccountIndex Master account index of the user who is performing the desert exit
  /// @param _assetIndex Asset index of the asset to be exited
  /// @param _totalBaseAmount Total base balance of the user for the asset to be exited
  /// @param proof Proof for the user assets
  function performDesert(
    uint48 _accountIndex,
    uint48 _masterAccountIndex,
    uint16 _assetIndex,
    uint128 _totalBaseAmount,
    bytes calldata proof
  ) external;

  /// @notice Cancels outstanding deposits, can be called only when desertMode is active
  /// @param _n Number of outstanding priority requests to be cancelled
  /// @param _priorityPubData Array of outstanding priority requests to be cancelled
  function cancelOutstandingDepositsForDesertMode(uint64 _n, bytes[] memory _priorityPubData) external;

  /// @notice Deposit to Lighter
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _amount Token amount
  /// @param _to The receiver L1 address
  function deposit(address _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256 _amount) external payable;

  /// @notice Deposit USDC to Lighter for multiple users
  /// @param _amount Array of USDC Token amounts
  /// @param _to Array of receiver L1 addresses
  /// @param _accountIndex Array of account index values, will be used in the future
  function depositBatch(uint64[] calldata _amount, address[] calldata _to, uint48[] calldata _accountIndex) external;

  /// @notice Change public key of a Lighter account
  /// @param _accountIndex Account index
  /// @param _apiKeyIndex API key index
  /// @param _pubKey New public key (40 bytes)
  function changePubKey(uint48 _accountIndex, uint8 _apiKeyIndex, bytes calldata _pubKey) external;

  /// @notice Register a new asset config
  /// @param assetIndex Asset index
  /// @param tokenAddress Token address
  /// @param withdrawalsEnabled Withdrawals enabled flag
  /// @param extensionMultiplier Extension multiplier of the asset
  /// @param tickSize Tick size of the asset
  /// @param depositCapTicks Deposit cap in ticks
  /// @param minDepositTicks Minimum deposit in ticks
  /// @dev This function is only callable by the governor
  function registerAssetConfig(
    uint16 assetIndex,
    address tokenAddress,
    uint8 withdrawalsEnabled,
    uint56 extensionMultiplier,
    uint128 tickSize,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  ) external;

  /// @notice Update existing asset config
  /// @param assetIndex Asset index
  /// @param withdrawalsEnabled Withdrawals enabled flag
  /// @param depositCapTicks Deposit cap in ticks
  /// @param minDepositTicks Minimum deposit in ticks
  /// @dev This function is only callable by the governor
  function updateAssetConfig(uint16 assetIndex, uint8 withdrawalsEnabled, uint64 depositCapTicks, uint64 minDepositTicks) external;

  /// @notice Set system config
  /// @param _params Config parameters
  function setSystemConfig(TxTypes.SetSystemConfig calldata _params) external;

  /// @notice Register a new asset to Lighter
  /// @param _decimals Number of decimals of the asset
  /// @param _symbol Symbol of the asset
  /// @param _params Asset parameters
  function registerAsset(uint8 _l1Decimals, uint8 _decimals, bytes32 _symbol, TxTypes.RegisterAsset calldata _params) external;

  /// @notice Update existing asset in Lighter
  /// @param _params Asset parameters to update
  function updateAsset(TxTypes.UpdateAsset calldata _params) external;

  /// @notice Create new market and an order book
  /// @param _size_decimals [metadata] Number of decimals to represent size of an order in the order book
  /// @param _price_decimals [metadata] Number of decimals to represent price of an order in the order book
  /// @param _symbol [metadata] symbol of the market
  /// @param _params Order book parameters
  function createMarket(uint8 _size_decimals, uint8 _price_decimals, bytes32 _symbol, TxTypes.CreateMarket calldata _params) external;

  /// @notice Updates the given order book, all values should be provided
  /// @param _params Order book parameters to update
  function updateMarket(TxTypes.UpdateMarket calldata _params) external;

  /// @notice Cancel all orders of a Lighter account
  /// @param _accountIndex Account index
  function cancelAllOrders(uint48 _accountIndex) external;

  /// @notice Withdraw from Lighter
  /// @param _accountIndex Account index
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _baseAmount Amount to withdraw
  function withdraw(uint48 _accountIndex, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) external;

  /// @notice Create an order for a Lighter account
  /// @param _accountIndex Account index
  /// @param _marketIndex Market index
  /// @param _baseAmount Amount of base token
  /// @param _price Price of the order
  /// @param _isAsk Flag to indicate if the order is ask or bid
  /// @param _orderType Order type
  function createOrder(uint48 _accountIndex, uint16 _marketIndex, uint48 _baseAmount, uint32 _price, uint8 _isAsk, uint8 _orderType) external;

  /// @notice Burn shares of an account in a public pool
  /// @param _accountIndex Account index
  /// @param _publicPoolIndex Public pool index
  /// @param _shareAmount Amount of shares to burn
  function burnShares(uint48 _accountIndex, uint48 _publicPoolIndex, uint64 _shareAmount) external;

  /// @notice Withdraws tokens from ZkLighter contract to the owner
  /// @param _owner Account address
  /// @param _assetIndex Asset index
  /// @param _baseAmount Base amount to withdraw
  function withdrawPendingBalance(address _owner, uint16 _assetIndex, uint128 _baseAmount) external;

  /// @notice Withdraws USDC tokens from ZkLighter contract to the owner (legacy)
  /// @param _owner Account address
  /// @param _baseAmount Base USDC amount to withdraw
  function withdrawPendingBalanceLegacy(address _owner, uint128 _baseAmount) external;

  /// @notice Sends tokens
  /// @param _token Token address
  /// @param _to Address of recipient
  /// @param _amount Amount of tokens to transfer
  /// @param _maxAmount Maximum possible amount of tokens to transfer to this account
  /// @return uint256 Amount of tokens transferred
  function transferERC20(IERC20 _token, address _to, uint256 _amount, uint256 _maxAmount) external returns (uint256);

  /// @notice Sends ETH
  /// @param _to Address of recipient
  /// @param _amount Amount of ETH to transfer
  /// @return uint256 Amount of ETH transferred
  function transferETH(address _to, uint256 _amount) external returns (uint256);

  /// @notice Reverts unverified batches
  /// @param _batchesToRevert Array of batches to be reverted
  /// @param _remainingBatch Last batch that is not reverted
  function revertBatches(Storage.StoredBatchInfo[] memory _batchesToRevert, Storage.StoredBatchInfo memory _remainingBatch) external;

  /// @notice Get pending balance that the user can withdraw
  /// @param _owner Owner account address
  /// @param _assetIndex Asset index
  /// @return uint128 Pending balance
  function getPendingBalance(address _owner, uint16 _assetIndex) external view returns (uint128);

  /// @notice Get pending balance that the user can withdraw in USDC (legacy)
  /// @param _owner Owner account address
  /// @return uint128 Pending USDC balance
  function getPendingBalanceLegacy(address _owner) external view returns (uint128);

  /// @notice Commit a new batch with at least one blob.
  /// @param newBatchData  New batch to be committed
  /// @param lastStoredBatch Last committed batch
  function commitBatch(CommitBatchInfo memory newBatchData, Storage.StoredBatchInfo memory lastStoredBatch) external;

  /// @notice Execute on chain operations in a verified batch
  /// @param batches Array of batches that contains the on chain operations to be executed
  /// @param onChainOperationsPubData Array of on chain operations that are verified and to be executed
  function executeBatches(Storage.StoredBatchInfo[] memory batches, bytes[] memory onChainOperationsPubData) external;

  /// @notice Verify a committed batch alongside its validity proof
  /// @param batch Batch to be verified
  /// @param proof Proof for the batch
  function verifyBatch(Storage.StoredBatchInfo memory batch, bytes memory proof) external;

  /// @notice Change the state root
  /// @param _lastStoredBatch Last committed batch
  /// @param _stateRoot New state root
  /// @param _validiumRoot New validium root
  /// @param proof Proof for the state root change
  function updateStateRoot(
    Storage.StoredBatchInfo calldata _lastStoredBatch,
    bytes32 _stateRoot,
    bytes32 _validiumRoot,
    bytes calldata proof
  ) external;

  /// @notice Change the treasury address
  /// @notice Can be called only by ZkLighter governor
  /// @param _newTreasury Address of the new treasury
  function setTreasury(address _newTreasury) external;

  /// @notice Change the insurance fund operator address
  /// @notice Can be called only by ZkLighter governor
  /// @param _newInsuranceFundOperator Address of the new insurance fund operator
  function setInsuranceFundOperator(address _newInsuranceFundOperator) external;
}
IZkLighterDesertMode.sol 13 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter DesertMode Interface
/// @author zkLighter Team
interface IZkLighterDesertMode {
  /// @notice Thrown when DesertMode is active
  error ZkLighter_DesertModeActive();

  /// @return True if desert mode is active, false otherwise
  function desertMode() external view returns (bool);
}
IZkLighterStateRootUpgradeVerifier.sol 9 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter State Root Upgrade Verifier Interface
/// @author zkLighter Team
interface IZkLighterStateRootUpgradeVerifier {
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) external view returns (bool success);
}
IZkLighterVerifier.sol 13 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter Verifier Interface
/// @author zkLighter Team
interface IZkLighterVerifier {
  /// @notice Verifies a SNARK proof of a batch
  /// @param proof The SNARK proof to verify
  /// @param public_inputs The public inputs for the proof
  /// @return success True if the proof is valid, false otherwise
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) external view returns (bool success);
}
Bytes.sol 104 lines
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.25;

/// @title zkLighter Bytes Library
/// @notice Implements helper functions to read bytes and convert them to other types
/// @author zkLighter Team
library Bytes {
  /// @dev Theoretically possible overflow of (_start + 0x8)
  function bytesToUInt64(bytes memory _bytes, uint256 _start) internal pure returns (uint64 r) {
    uint256 offset = _start + 0x8;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x7)
  function bytesToUInt56(bytes memory _bytes, uint256 _start) internal pure returns (uint56 r) {
    uint256 offset = _start + 0x7;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  /// @dev Theoretically possible overflow of (_start + 0x6)
  function bytesToUInt48(bytes memory _bytes, uint256 _start) internal pure returns (uint48 r) {
    uint256 offset = _start + 0x6;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x4)
  function bytesToUInt32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 r) {
    uint256 offset = _start + 0x4;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x3)
  function bytesToUInt24(bytes memory _bytes, uint256 _start) internal pure returns (uint24 r) {
    uint256 offset = _start + 0x3;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x2)
  function bytesToUInt16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 r) {
    uint256 offset = _start + 0x2;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  /// @dev Theoretically possible overflow of (_offset + 0x8)
  function readUInt64(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint64 r) {
    newOffset = _offset + 8;
    r = bytesToUInt64(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 7)
  function readUInt56(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint56 r) {
    newOffset = _offset + 7;
    r = bytesToUInt56(_data, _offset);
  }

  /// @dev Theoretically possible overflow of (_offset + 0x6)
  function readUInt48(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint48 r) {
    newOffset = _offset + 6;
    r = bytesToUInt48(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 4)
  function readUInt32(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint32 r) {
    newOffset = _offset + 4;
    r = bytesToUInt32(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 3)
  function readUInt24(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint24 r) {
    newOffset = _offset + 3;
    r = bytesToUInt24(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 2)
  function readUInt16(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint16 r) {
    newOffset = _offset + 2;
    r = bytesToUInt16(_data, _offset);
  }

  /// @dev Theoretically possible overflow of (_offset + 0x1)
  function readUInt8(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint8 r) {
    newOffset = _offset + 1;
    r = uint8(_data[_offset]);
  }
}
TxTypes.sol 427 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "./Bytes.sol";

/// @title zkLighter TxTypes Library
/// @notice Implements helper functions to serialize and deserialize tx types
/// @author zkLighter Team
library TxTypes {
  /// @notice Market types
  enum MarketType {
    Perps, // 0
    Spot // 1
  }

  /// @notice Asset margin modes
  enum AssetMarginMode {
    Disabled, // 0
    Enabled // 1
  }

  /// @notice Asset destination / source type
  enum RouteType {
    Perps, // 0
    Spot // 1
  }

  /// @notice zklighter priority request types
  uint8 constant PriorityPubDataTypeEmpty = 40;
  uint8 constant PriorityPubDataTypeL1Deposit = 41;
  uint8 constant PriorityPubDataTypeL1ChangePubKey = 42;
  uint8 constant PriorityPubDataTypeL1CreateMarket = 43;
  uint8 constant PriorityPubDataTypeL1UpdateMarket = 44;
  uint8 constant PriorityPubDataTypeL1CancelAllOrders = 45;
  uint8 constant PriorityPubDataTypeL1Withdraw = 46;
  uint8 constant PriorityPubDataTypeL1CreateOrder = 47;
  uint8 constant PriorityPubDataTypeL1BurnShares = 48;
  uint8 constant PriorityPubDataTypeL1RegisterAsset = 49;
  uint8 constant PriorityPubDataTypeL1UpdateAsset = 50;
  uint8 constant PriorityPubDataTypeL1UnstakeAssets = 51;
  uint8 constant PriorityPubDataTypeL1SetSystemConfig = 52;

  /// @notice zklighter onchain transaction types
  enum OnChainPubDataType {
    Empty,
    USDCWithdraw,
    Withdraw
  }

  uint32 internal constant USDCWithdrawLogSize = 15; // 1 byte for type, 6 bytes for accountIndex, 8 bytes for usdcAmount
  uint32 internal constant WithdrawLogSize = 17; // 1 byte for type, 2 bytes for assetIndex, 6 bytes for accountIndex, 8 bytes for amount

  enum OrderType {
    LimitOrder,
    MarketOrder
  }

  uint256 internal constant DEPOSIT_PUB_DATA_SIZE = 38;

  struct Deposit {
    uint48 accountIndex;
    address toAddress;
    uint16 assetIndex;
    RouteType routeType;
    uint64 baseAmount;
  }

  /// @notice Serialize deposit pubData
  function writeDepositPubDataForPriorityQueue(Deposit memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1Deposit), _tx.accountIndex, _tx.toAddress, _tx.assetIndex, _tx.routeType, _tx.baseAmount);
  }

  /// @notice Deserialize deposit pubData
  function readDepositForDesertMode(bytes memory _data) internal pure returns (uint48 accountIndex, uint16 assetIndex, uint64 baseAmount) {
    uint256 _offset = 1; // Skipping the tx type
    (_offset, accountIndex) = Bytes.readUInt48(_data, _offset);
    _offset += 20; // Skipping the address
    (_offset, assetIndex) = Bytes.readUInt16(_data, _offset);
    _offset++; // Skipping the route type
    (_offset, baseAmount) = Bytes.readUInt64(_data, _offset);
    return (accountIndex, assetIndex, baseAmount);
  }

  struct L1Withdraw {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint16 assetIndex;
    RouteType routeType;
    uint64 baseAmount;
  }

  /// @notice Serialize withdraw pubData
  function writeWithdrawPubDataForPriorityQueue(L1Withdraw memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1Withdraw),
      _tx.accountIndex,
      _tx.masterAccountIndex,
      _tx.assetIndex,
      _tx.routeType,
      _tx.baseAmount
    );
  }

  struct Withdraw {
    uint48 masterAccountIndex;
    uint16 assetIndex;
    uint64 baseAmount;
  }

  /// @notice Deserialize withdraw pubData
  function readWithdrawOnChainLog(bytes memory _data, uint256 _offset) internal pure returns (Withdraw memory parsed, uint256 newOffset) {
    _offset++; // Skipping the type
    (_offset, parsed.masterAccountIndex) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.assetIndex) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.baseAmount) = Bytes.readUInt64(_data, _offset);
    return (parsed, _offset);
  }

  struct USDCWithdraw {
    uint48 masterAccountIndex;
    uint64 usdcAmount;
  }

  /// @notice Deserialize USDC withdraw pubData
  function readUSDCWithdrawOnChainLog(bytes memory _data, uint256 _offset) internal pure returns (USDCWithdraw memory parsed, uint256 newOffset) {
    _offset++; // Skipping the type
    (_offset, parsed.masterAccountIndex) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.usdcAmount) = Bytes.readUInt64(_data, _offset);
    return (parsed, _offset);
  }

  uint8 internal constant PACKED_COMMON_PERPS_DATA_BYTES = 55;

  struct CommonPerpsData {
    uint32 takerFee;
    uint32 makerFee;
    uint32 liquidationFee;
    uint48 minBaseAmount;
    uint48 minQuoteAmount;
    uint16 defaultInitialMarginFraction;
    uint16 minInitialMarginFraction;
    uint16 maintenanceMarginFraction;
    uint16 closeOutMarginFraction;
    uint32 interestRate;
    uint24 fundingClampSmall;
    uint24 fundingClampBig;
    uint56 openInterestLimit;
    uint48 orderQuoteLimit;
  }

  uint8 internal constant PACKED_CREATE_MARKET_PERPS_BYTES = 4 + PACKED_COMMON_PERPS_DATA_BYTES;

  struct CreateMarketPerpsData {
    uint32 quoteMultiplier;
    CommonPerpsData common;
  }

  function readCreateMarketPerpsData(bytes memory _data) internal pure returns (CreateMarketPerpsData memory parsed) {
    if (_data.length != PACKED_CREATE_MARKET_PERPS_BYTES) {
      revert("Invalid packed create market perps data length");
    }
    uint256 _offset;
    (_offset, parsed.quoteMultiplier) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.liquidationFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.defaultInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.minInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.maintenanceMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.closeOutMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.interestRate) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.fundingClampSmall) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.fundingClampBig) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.openInterestLimit) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  uint8 internal constant PACKED_COMMON_SPOT_DATA_BYTES = 26;

  struct CommonSpotData {
    uint32 takerFee;
    uint32 makerFee;
    uint48 minBaseAmount;
    uint48 minQuoteAmount;
    uint48 orderQuoteLimit;
  }

  uint8 internal constant PACKED_CREATE_MARKET_SPOT_BYTES = 18 + PACKED_COMMON_SPOT_DATA_BYTES;

  struct CreateMarketSpotData {
    uint16 baseAssetIndex;
    uint16 quoteAssetIndex;
    uint56 sizeExtensionMultiplier;
    uint56 quoteExtensionMultiplier;
    CommonSpotData common;
  }

  function readCreateMarketSpotData(bytes memory _data) internal pure returns (CreateMarketSpotData memory parsed) {
    if (_data.length != PACKED_CREATE_MARKET_SPOT_BYTES) {
      revert("Invalid packed create market spot data length");
    }
    uint256 _offset;
    (_offset, parsed.baseAssetIndex) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.quoteAssetIndex) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.sizeExtensionMultiplier) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.quoteExtensionMultiplier) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  struct CreateMarket {
    uint16 marketIndex;
    MarketType marketType;
    bytes marketData;
  }

  /// @notice Serialize create order book pubData, it does not include metadata
  function writeCreateMarketPubDataForPriorityQueue(CreateMarket memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1CreateMarket), _tx.marketIndex, _tx.marketType, _tx.marketData);
  }

  /// @notice Serialize create order book pubData, includes metadata
  function writeCreateMarketPubDataForPriorityQueueWithMetadata(
    bytes memory _data,
    uint8 size_decimals,
    uint8 price_decimals,
    bytes32 symbol
  ) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(_data, size_decimals, price_decimals, symbol);
  }

  struct UpdateMarket {
    uint16 marketIndex;
    MarketType marketType;
    bytes marketData;
  }

  uint8 internal constant PACKED_UPDATE_MARKET_PERPS_BYTES = 1 + PACKED_COMMON_PERPS_DATA_BYTES;

  struct UpdateMarketPerps {
    uint8 status;
    CommonPerpsData common;
  }

  function readUpdateMarketPerpsData(bytes memory _data) internal pure returns (UpdateMarketPerps memory parsed) {
    if (_data.length != PACKED_UPDATE_MARKET_PERPS_BYTES) {
      revert("Invalid packed update market perps data length");
    }
    uint256 _offset;
    (_offset, parsed.status) = Bytes.readUInt8(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.liquidationFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.defaultInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.minInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.maintenanceMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.closeOutMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.interestRate) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.fundingClampSmall) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.fundingClampBig) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.openInterestLimit) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  uint8 internal constant PACKED_UPDATE_MARKET_SPOT_BYTES = 1 + PACKED_COMMON_SPOT_DATA_BYTES;

  struct UpdateMarketSpot {
    uint8 status;
    CommonSpotData common;
  }

  function readUpdateMarketSpotData(bytes memory _data) internal pure returns (UpdateMarketSpot memory parsed) {
    if (_data.length != PACKED_UPDATE_MARKET_SPOT_BYTES) {
      revert("Invalid packed update market spot data length");
    }
    uint256 _offset;
    (_offset, parsed.status) = Bytes.readUInt8(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  /// @notice Serialize update order book pubData
  function writeUpdateMarketPubDataForPriorityQueue(UpdateMarket memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1UpdateMarket), _tx.marketIndex, _tx.marketType, _tx.marketData);
  }

  struct CreateOrder {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint16 marketIndex;
    uint48 baseAmount;
    uint32 price;
    uint8 isAsk;
    uint8 orderType;
  }

  /// @notice Serialize create order pubData
  function writeCreateOrderPubDataForPriorityQueue(CreateOrder memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1CreateOrder),
      _tx.accountIndex,
      _tx.masterAccountIndex,
      _tx.marketIndex,
      _tx.baseAmount,
      _tx.price,
      _tx.isAsk,
      _tx.orderType
    );
  }

  struct BurnShares {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint48 publicPoolIndex;
    uint64 sharesAmount;
  }

  /// @notice Serialize burn shares pubData
  function writeBurnSharesPubDataForPriorityQueue(BurnShares memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1BurnShares), _tx.accountIndex, _tx.masterAccountIndex, _tx.publicPoolIndex, _tx.sharesAmount);
  }

  struct CancelAllOrders {
    uint48 accountIndex;
    uint48 masterAccountIndex;
  }

  /// @notice Serialize cancel all orders pubData
  function writeCancelAllOrdersPubDataForPriorityQueue(CancelAllOrders memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1CancelAllOrders), _tx.accountIndex, _tx.masterAccountIndex);
  }

  struct ChangePubKey {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint8 apiKeyIndex;
    bytes pubKey;
  }

  /// @notice Serialize change pub key pubData
  function writeChangePubKeyPubDataForPriorityQueue(ChangePubKey memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1ChangePubKey), _tx.accountIndex, _tx.masterAccountIndex, _tx.apiKeyIndex, _tx.pubKey);
  }

  struct SetSystemConfig {
    uint48 liquidityPoolIndex;
    uint48 stakingPoolIndex;
    uint48 liquidityPoolCooldownPeriod;
    uint48 stakingPoolLockupPeriod;
  }

  /// @notice Serialize create asset pubData
  function writeSetSystemConfigPubDataForPriorityQueue(SetSystemConfig memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1SetSystemConfig),
      _tx.liquidityPoolIndex,
      _tx.stakingPoolIndex,
      _tx.liquidityPoolCooldownPeriod,
      _tx.stakingPoolLockupPeriod
    );
  }

  struct RegisterAsset {
    uint16 assetIndex; // Asset index of the token being registered
    uint56 extensionMultiplier; // Lighter internal asset extension multiplier
    uint64 minL2TransferAmount; // Minimum L2 transfer amount for the asset
    uint64 minL2WithdrawalAmount; // Minimum L2 withdrawal amount for the asset
    uint8 marginMode; // 0 if disabled, 1 if enabled
  }

  /// @notice Serialize create asset pubData
  function writeRegisterAssetPubDataForPriorityQueue(RegisterAsset memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1RegisterAsset),
      _tx.assetIndex,
      _tx.extensionMultiplier,
      _tx.minL2TransferAmount,
      _tx.minL2WithdrawalAmount,
      _tx.marginMode
    );
  }

  /// @notice Serialize create order book pubData, includes metadata
  function writeRegisterAssetPubDataForPriorityQueueWithMetadata(
    bytes memory _data,
    uint8 l1Decimals,
    uint8 decimals,
    uint128 tickSize,
    address tokenAddress,
    bytes32 symbol
  ) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(_data, l1Decimals, decimals, tickSize, tokenAddress, symbol);
  }

  struct UpdateAsset {
    uint16 assetIndex; // Asset index of the token being updated
    uint64 minL2TransferAmount; // Minimum L2 transfer amount for the asset
    uint64 minL2WithdrawalAmount; // Minimum L2 withdrawal amount for the asset
    uint8 marginMode; // 0 if disabled, 1 if enabled
  }

  /// @notice Serialize update asset pubData
  function writeUpdateAssetPubDataForPriorityQueue(UpdateAsset memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1UpdateAsset),
      _tx.assetIndex,
      _tx.minL2TransferAmount,
      _tx.minL2WithdrawalAmount,
      _tx.marginMode
    );
  }
}
Storage.sol 158 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "./interfaces/IZkLighterDesertMode.sol";
import "./interfaces/IZkLighterVerifier.sol";
import "./interfaces/IDesertVerifier.sol";
import "./interfaces/IGovernance.sol";
import "./AdditionalZkLighter.sol";
import "./Config.sol";

/// @title zkLighter Storage Contract
/// @author zkLighter Team
contract Storage is IZkLighterDesertMode, Config {
  // Public tree roots
  bytes32 public stateRoot;
  bytes32 public validiumRoot;

  struct PriorityRequest {
    bytes32 prefixHash;
    uint64 expirationTimestamp;
  }

  /// @dev Priority Request mapping
  /// @dev Requests are indexed by their receiving order
  mapping(uint64 => PriorityRequest) internal priorityRequests;

  /// @notice Priority operation struct
  /// @dev Contains request type and hashed pubData
  struct OnChainL2Request {
    bytes20 hashedPubData;
    uint64 priorityRequestOffset;
  }

  enum MarketStatus {
    NONE,
    ACTIVE
  }

  /// @dev Deprecated: L2 Request mapping for L2 transactions that needs to be executed in the base layer
  mapping(uint64 => OnChainL2Request) internal __DEPRECATED_onChainL2Requests;

  /// @dev Verifier contract, used for verifying batch execution proofs
  IZkLighterVerifier internal verifier;

  /// @dev Desert verifier contract, used for verifying desert mode proofs
  IDesertVerifier internal desertVerifier;

  /// @dev Governance contract, stores the governor of the network
  IGovernance internal governance;

  /// @dev Additional zkLighter implementation contract (code size limitations)
  AdditionalZkLighter internal additionalZkLighter;

  /// @dev Number of priority requests committed
  uint64 public committedPriorityRequestCount;
  /// @dev Number of priority requests committed and verified
  uint64 public verifiedPriorityRequestCount;
  /// @dev Number of priority requests committed, verified and executed
  uint64 public executedPriorityRequestCount;
  /// @dev Number of queued priority requests waiting to be executed
  uint64 public openPriorityRequestCount;

  /// @dev Number of batches committed
  uint64 public committedBatchesCount;
  /// @dev Number of batches committed and verified
  uint64 public verifiedBatchesCount;
  /// @dev Number of batches committed, verified and executed
  uint64 public executedBatchesCount;

  /// @dev Number of queued batches that have onChainOperations waiting to be executed
  uint64 public pendingOnChainBatchesCount;
  /// @dev Number of queued batches that have onChainOperations executed
  uint64 public executedOnChainBatchesCount;

  bytes32 public lastVerifiedStateRoot;
  bytes32 public lastVerifiedValidiumRoot;
  uint64 public lastVerifiedEndBlockNumber;

  struct StoredBatchInfo {
    uint64 batchNumber;
    uint64 endBlockNumber;
    uint32 batchSize;
    uint64 startTimestamp;
    uint64 endTimestamp;
    uint32 priorityRequestCount;
    bytes32 prefixPriorityRequestHash;
    bytes32 onChainOperationsHash;
    bytes32 stateRoot;
    bytes32 validiumRoot;
    bytes32 commitment;
  }

  /// @dev Stores hashed StoredBatchInfo indexed by the batchNumber
  mapping(uint64 => bytes32) public storedBatchHashes;

  struct ExecutionQueueItem {
    uint64 batchNumber;
    uint64 totalPriorityRequests;
  }

  /// @dev Stores if a batch needs to be executed, indexed by the pendingOnChainBatchesCount and
  /// @dev executedOnChainBatchesCount, value is the batchNumber
  mapping(uint64 => ExecutionQueueItem) internal onChainExecutionQueue;

  /// @dev Flag indicates that desert (exit hatch) mode is triggered
  /// @dev Once desert mode is triggered, it can not be reverted
  bool public override desertMode;

  /// @dev Deprecated: Added a new mapping(uint48 => bool) internal accountPerformedDesert;
  mapping(uint32 => bool) internal __DEPRECATED_performedDesert;

  uint8 internal constant FILLED_GAS_RESERVE_VALUE = 0xff; // Used for setting gas reserve value, so that the slot will not be emptied with 0 balance
  struct DEPRECATED_PendingBalance {
    uint128 balanceToWithdraw;
    uint8 gasReserveValue;
  }

  /// @notice Address that collects fees from listed markets
  address public treasury;
  /// @notice Address that operates the insurance fund
  address public insuranceFundOperator;
  /// @notice Index of the last registered account in the network including the system accounts
  uint48 public lastAccountIndex;
  /// @notice Account address to account id mapping, excluding the system accounts
  mapping(address => uint48) public addressToAccountIndex;

  /// @dev Base layer withdrawable USDC balances for each master account index
  /// @dev Deprecated: use pendingAssetBalances instead
  mapping(uint48 => DEPRECATED_PendingBalance) internal DEPRECATED_pendingBalance;

  /// @dev Deprecated state root updates mapping, moved to ExtendableStorage
  mapping(uint64 => bytes32) public __DEPRECATED_stateRootUpdates;

  modifier onlyActive() {
    if (desertMode) {
      // Desert mode activated
      revert ZkLighter_DesertModeActive();
    }
    _;
  }

  function hashStoredBatchInfo(StoredBatchInfo memory _batch) internal pure returns (bytes32) {
    return keccak256(abi.encode(_batch));
  }

  function getAccountIndexFromAddress(address _address) internal view returns (uint48) {
    uint48 _accountIndex = addressToAccountIndex[_address];
    if (_accountIndex == 0) {
      if (_address == treasury) {
        return TREASURY_ACCOUNT_INDEX;
      } else if (_address == insuranceFundOperator) {
        return INSURANCE_FUND_OPERATOR_ACCOUNT_INDEX;
      }
      return NIL_ACCOUNT_INDEX;
    }
    return _accountIndex;
  }
}
ZkLighter.sol 916 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IZkLighter.sol";
import "./interfaces/IZkLighterVerifier.sol";
import "./interfaces/IDesertVerifier.sol";
import "./lib/Bytes.sol";
import "./lib/TxTypes.sol";
import "./Storage.sol";
import "./ExtendableStorage.sol";

/// @title zkLighter Contract
/// @author zkLighter Team
contract ZkLighter is IZkLighter, Storage, ReentrancyGuardUpgradeable, ExtendableStorage {
  address private immutable zklighterImplementation;

  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
  // * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure */
  // * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */
  // * Avoid leaving a contract uninitialized. */
  // * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation */
  // * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke */
  // * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: */
  constructor() {
    zklighterImplementation = address(this);

    _disableInitializers();
  }

  /// @notice ZkLighter contract initialization.
  /// @param initializationParameters Encoded representation of initialization parameters:
  /// @dev _governanceAddress The address of Governance contract
  /// @dev _verifierAddress The address of Verifier contract
  /// @dev _additionalZkLighter The address of Additional zkLighter contract
  /// @dev _desertVerifier The address of Desert Verifier contract
  /// @dev _genesisStateRoot Genesis blocks (first block) state tree root hash
  /// @dev _genesisValidiumRoot Genesis blocks (first block) validium tree root hash
  function initialize(bytes calldata initializationParameters) external initializer {
    if (address(this) == zklighterImplementation) {
      revert ZkLighter_CannotBeInitialisedByImpl();
    }

    __ReentrancyGuard_init();

    (
      address _governanceAddress,
      address _verifierAddress,
      address _additionalZkLighter,
      address _desertVerifier,
      bytes32 _genesisStateRoot,
      bytes32 _genesisValidiumRoot
    ) = abi.decode(initializationParameters, (address, address, address, address, bytes32, bytes32));

    if (!_hasCode(_governanceAddress) || !_hasCode(_verifierAddress) || !_hasCode(_additionalZkLighter) || !_hasCode(_desertVerifier)) {
      revert ZkLighter_InvalidInitializeParameters();
    }

    verifier = IZkLighterVerifier(_verifierAddress);
    governance = IGovernance(_governanceAddress);
    additionalZkLighter = AdditionalZkLighter(_additionalZkLighter);
    desertVerifier = IDesertVerifier(_desertVerifier);

    StoredBatchInfo memory genesisBatchInfo = StoredBatchInfo({
      batchNumber: 0,
      endBlockNumber: 0,
      batchSize: 0,
      startTimestamp: 0,
      endTimestamp: 0,
      priorityRequestCount: 0,
      prefixPriorityRequestHash: 0,
      onChainOperationsHash: 0,
      stateRoot: _genesisStateRoot,
      validiumRoot: _genesisValidiumRoot,
      commitment: bytes32(0)
    });
    stateRoot = _genesisStateRoot;
    storedBatchHashes[0] = hashStoredBatchInfo(genesisBatchInfo);

    _registerDefaultAssetConfigs();

    lastAccountIndex = 2; // First 3 accounts are reserved for system accounts
  }

  /// @notice ZkLighter contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function
  /// @param upgradeParameters Encoded representation of upgrade parameters
  function upgrade(bytes calldata upgradeParameters) external nonReentrant {
    if (address(this) == zklighterImplementation) {
      revert ZkLighter_OnlyProxyCanCallUpgrade();
    }

    // Commit to the initialization parameters to ensure parameters are known at the time of upgrade initialization
    bytes32 upgradeParametersHash = keccak256(upgradeParameters);
    // Commits to 0 address for _additionalZkLighter, _desertVerifier and _stateRootUpgradeVerifier
    bytes32 initializationParametersCommitment = 0xb11bb680dab3fb9238aa513aacc13268188c937de0f36b523827f67163969174;
    if (upgradeParametersHash != initializationParametersCommitment) {
      revert ZkLighter_InvalidUpgradeParameters();
    }

    (address _additionalZkLighter, address _desertVerifier, address _stateRootUpgradeVerifier) = abi.decode(
      upgradeParameters,
      (address, address, address)
    );

    if (_additionalZkLighter != address(0)) {
      if (!_hasCode(_additionalZkLighter)) {
        revert ZkLighter_InvalidUpgradeParameters();
      }
      additionalZkLighter = AdditionalZkLighter(_additionalZkLighter);
    }

    if (_desertVerifier != address(0)) {
      if (!_hasCode(_desertVerifier)) {
        revert ZkLighter_InvalidUpgradeParameters();
      }
      desertVerifier = IDesertVerifier(_desertVerifier);
    }

    if (_stateRootUpgradeVerifier != address(0)) {
      if (!_hasCode(_stateRootUpgradeVerifier)) {
        revert ZkLighter_InvalidUpgradeParameters();
      }
      stateRootUpgradeVerifier = IZkLighterStateRootUpgradeVerifier(_stateRootUpgradeVerifier);
    }
  }

  function _registerDefaultAssetConfigs() internal {
    address ethAddress = address(0);
    uint56 ethExtensionMultiplier = 100;
    uint128 ethTickSize = 10 ** 10; // 0.00000001 ETH
    assetConfigs[NATIVE_ASSET_INDEX] = AssetConfig({
      tokenAddress: ethAddress,
      withdrawalsEnabled: 1,
      extensionMultiplier: ethExtensionMultiplier,
      tickSize: ethTickSize,
      depositCapTicks: MAX_DEPOSIT_CAP_TICKS,
      minDepositTicks: 100_000 // 0.001 ETH
    });
    tokenToAssetIndex[ethAddress] = NATIVE_ASSET_INDEX;
    emit RegisterAssetConfig(NATIVE_ASSET_INDEX, ethAddress, 1, ethExtensionMultiplier, ethTickSize, MAX_DEPOSIT_CAP_TICKS, 100_000);

    address usdcAddress = address(governance.usdc());
    uint56 usdcExtensionMultiplier = 1_000_000;
    uint128 usdcTickSize = 1;
    assetConfigs[USDC_ASSET_INDEX] = AssetConfig({
      tokenAddress: usdcAddress,
      withdrawalsEnabled: 1,
      extensionMultiplier: usdcExtensionMultiplier,
      tickSize: usdcTickSize,
      depositCapTicks: MAX_DEPOSIT_CAP_TICKS,
      minDepositTicks: 1_000_000 // 1 USDC
    });
    tokenToAssetIndex[usdcAddress] = USDC_ASSET_INDEX;
    emit RegisterAssetConfig(USDC_ASSET_INDEX, usdcAddress, 1, usdcExtensionMultiplier, usdcTickSize, MAX_DEPOSIT_CAP_TICKS, 1_000_000);
  }

  /// @inheritdoc IZkLighter
  function registerAssetConfig(
    uint16 assetIndex,
    address tokenAddress,
    uint8 withdrawalsEnabled,
    uint56 extensionMultiplier,
    uint128 tickSize,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    if (assetIndex == NATIVE_ASSET_INDEX || assetConfigs[assetIndex].tokenAddress != address(0)) {
      revert ZkLighter_InvalidAssetIndex();
    }
    if (assetIndex < MIN_ASSET_INDEX || assetIndex > MAX_ASSET_INDEX) {
      revert ZkLighter_InvalidAssetIndex();
    }
    if (tokenAddress == address(0) || tokenToAssetIndex[tokenAddress] != 0) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (withdrawalsEnabled != 0 && withdrawalsEnabled != 1) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (!_hasCode(tokenAddress)) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (tickSize == 0 || depositCapTicks == 0 || minDepositTicks == 0) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (tickSize > MAX_TICK_SIZE || depositCapTicks > MAX_DEPOSIT_CAP_TICKS || minDepositTicks > depositCapTicks) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    uint128 extendedDepositCapTicks = uint128(extensionMultiplier) * uint128(depositCapTicks);
    if (
      extensionMultiplier == 0 || extensionMultiplier > MAX_ASSET_EXTENSION_MULTIPLIER || extendedDepositCapTicks > MAX_EXTENDED_DEPOSIT_CAP_TICKS
    ) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    assetConfigs[assetIndex] = AssetConfig({
      tokenAddress: tokenAddress,
      withdrawalsEnabled: withdrawalsEnabled,
      extensionMultiplier: extensionMultiplier,
      tickSize: tickSize,
      depositCapTicks: depositCapTicks,
      minDepositTicks: minDepositTicks
    });
    tokenToAssetIndex[tokenAddress] = assetIndex;
    emit RegisterAssetConfig(assetIndex, tokenAddress, withdrawalsEnabled, extensionMultiplier, tickSize, depositCapTicks, minDepositTicks);
  }

  /// @inheritdoc IZkLighter
  function updateAssetConfig(
    uint16 assetIndex,
    uint8 withdrawalsEnabled,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    AssetConfig memory config = assetConfigs[assetIndex];
    if (assetIndex != NATIVE_ASSET_INDEX && config.tokenAddress == address(0)) {
      revert ZkLighter_InvalidAssetIndex();
    }

    if (withdrawalsEnabled != 0 && withdrawalsEnabled != 1) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    // Withdrawals can not be disabled if they are enabled already
    // Only transition allowed for this parameter is from disabled to enabled
    if (withdrawalsEnabled == 0 && config.withdrawalsEnabled == 1) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (depositCapTicks == 0 || minDepositTicks == 0) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (depositCapTicks > MAX_DEPOSIT_CAP_TICKS || minDepositTicks > depositCapTicks) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    uint128 extendedDepositCapTicks = uint128(config.extensionMultiplier) * uint128(depositCapTicks);
    if (extendedDepositCapTicks > MAX_EXTENDED_DEPOSIT_CAP_TICKS) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    assetConfigs[assetIndex] = AssetConfig({
      tokenAddress: config.tokenAddress,
      withdrawalsEnabled: withdrawalsEnabled,
      extensionMultiplier: config.extensionMultiplier,
      tickSize: config.tickSize,
      depositCapTicks: depositCapTicks,
      minDepositTicks: minDepositTicks
    });
    emit UpdateAssetConfig(assetIndex, withdrawalsEnabled, depositCapTicks, minDepositTicks);
  }

  /// @inheritdoc IZkLighter
  function setSystemConfig(TxTypes.SetSystemConfig calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function registerAsset(uint8 _l1Decimals, uint8 _decimals, bytes32 _symbol, TxTypes.RegisterAsset calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function updateAsset(TxTypes.UpdateAsset calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function deposit(address _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256 _amount) external payable {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function depositBatch(uint64[] calldata _amount, address[] calldata _to, uint48[] calldata _accountIndex) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function changePubKey(uint48 _accountIndex, uint8 _apiKeyIndex, bytes calldata _pubKey) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function createMarket(uint8 _size_decimals, uint8 _price_decimals, bytes32 _symbol, TxTypes.CreateMarket calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function updateMarket(TxTypes.UpdateMarket calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function cancelAllOrders(uint48 _accountIndex) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function withdraw(uint48 _accountIndex, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function createOrder(uint48 _accountIndex, uint16 _marketIndex, uint48 _baseAmount, uint32 _price, uint8 _isAsk, uint8 _orderType) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function burnShares(uint48 _accountIndex, uint48 _publicPoolIndex, uint64 _shareAmount) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function updateStateRoot(StoredBatchInfo calldata _lastStoredBatch, bytes32 _stateRoot, bytes32 _validiumRoot, bytes calldata proof) external {
    delegateAdditional();
  }

  function createExitCommitment(
    uint256 stateRoot,
    uint48 _accountIndex,
    uint48 _masterAccountIndex,
    uint16 _assetIndex,
    uint128 _totalBaseAmount
  ) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(stateRoot, _accountIndex, _masterAccountIndex, _assetIndex, _totalBaseAmount));
  }

  /// @notice Performs exit from zkLighter in desert mode
  function performDesert(
    uint48 _accountIndex,
    uint48 _masterAccountIndex,
    uint16 _assetIndex,
    uint128 _totalBaseAmount,
    bytes calldata proof
  ) external nonReentrant {
    // Must be in desert mode
    if (!desertMode) {
      revert ZkLighter_DesertModeInactive();
    }

    if (accountPerformedDesertForAsset[_assetIndex][_accountIndex]) {
      revert ZkLighter_AccountAlreadyPerformedDesertForAsset();
    }

    uint256[] memory inputs = new uint256[](1);
    bytes32 commitment = createExitCommitment(uint256(stateRoot), _accountIndex, _masterAccountIndex, _assetIndex, _totalBaseAmount);
    inputs[0] = uint256(commitment) % BN254_MODULUS;
    bool success = desertVerifier.Verify(proof, inputs);
    if (!success) {
      revert ZkLighter_DesertVerifyProofFailed();
    }

    increaseBalanceToWithdraw(_masterAccountIndex, _assetIndex, _totalBaseAmount);
    accountPerformedDesertForAsset[_assetIndex][_accountIndex] = true;
  }

  /// @inheritdoc IZkLighter
  function cancelOutstandingDepositsForDesertMode(uint64 _n, bytes[] memory _priorityPubData) external nonReentrant {
    // Must be in desert mode
    if (!desertMode) {
      revert ZkLighter_DesertModeInactive();
    }

    if (openPriorityRequestCount == 0 || _n == 0) {
      revert ZkLighter_NoOutstandingDepositsForCancelation();
    }

    if (_n > openPriorityRequestCount || _n != _priorityPubData.length) {
      revert ZkLighter_InvalidParamsForCancelOutstandingDeposits();
    }

    uint64 startIndex = executedPriorityRequestCount;

    bytes32 pubDataPrefixHash = bytes32(0);
    if (executedPriorityRequestCount > 0) {
      pubDataPrefixHash = priorityRequests[executedPriorityRequestCount - 1].prefixHash;
    }

    uint64 currentPubDataIdx = 0;
    for (uint64 id = startIndex; id < startIndex + _n; ++id) {
      if (_priorityPubData[currentPubDataIdx].length > MAX_PRIORITY_REQUEST_PUBDATA_SIZE || _priorityPubData[currentPubDataIdx].length == 0) {
        revert ZkLighter_InvalidParamsForCancelOutstandingDeposits();
      }

      bytes memory paddedPubData = new bytes(MAX_PRIORITY_REQUEST_PUBDATA_SIZE);
      for (uint256 i = 0; i < _priorityPubData[currentPubDataIdx].length; ++i) {
        paddedPubData[i] = _priorityPubData[currentPubDataIdx][i];
      }

      pubDataPrefixHash = keccak256(abi.encodePacked(pubDataPrefixHash, paddedPubData));
      if (pubDataPrefixHash != priorityRequests[id].prefixHash) {
        revert ZkLighter_DepositPubdataHashMismatch();
      }

      if (uint8(_priorityPubData[currentPubDataIdx][0]) == TxTypes.PriorityPubDataTypeL1Deposit) {
        if (_priorityPubData[currentPubDataIdx].length != TxTypes.DEPOSIT_PUB_DATA_SIZE) {
          revert ZkLighter_DepositPubdataHashMismatch();
        }
        bytes memory depositPubdata = _priorityPubData[currentPubDataIdx];
        (uint48 accountIndex, uint16 assetIndex, uint64 baseAmount) = TxTypes.readDepositForDesertMode(depositPubdata);
        increaseBalanceToWithdraw(accountIndex, assetIndex, baseAmount);
      }

      ++currentPubDataIdx;
    }

    openPriorityRequestCount -= _n;
    executedPriorityRequestCount += _n;
  }

  /// @inheritdoc IZkLighter
  function commitBatch(CommitBatchInfo calldata newBatchData, StoredBatchInfo calldata lastStoredBatch) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    if (newBatchData.pubdataCommitments.length == 0) {
      revert ZkLighter_InvalidBlobCommitmentParams();
    }
    uint8 pubDataMode = uint8(bytes1(newBatchData.pubdataCommitments[0]));
    if (pubDataMode != uint8(PubDataMode.Blob)) {
      revert ZkLighter_InvalidPubDataMode();
    }
    if (newBatchData.endBlockNumber <= lastStoredBatch.endBlockNumber) {
      revert ZkLighter_NonIncreasingBlockNumber();
    }
    if (newBatchData.endBlockNumber != lastStoredBatch.endBlockNumber + newBatchData.batchSize) {
      revert ZkLighter_InvalidBatchSize();
    }
    if (newBatchData.startTimestamp < lastStoredBatch.endTimestamp || newBatchData.endTimestamp < newBatchData.startTimestamp) {
      revert ZkLighter_NonIncreasingTimestamp();
    }
    if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(lastStoredBatch)) {
      revert ZkLighter_StoredBatchInfoMismatch();
    }
    uint64 lastPriorityRequestToCommitCount = committedPriorityRequestCount + newBatchData.priorityRequestCount;
    if (executedPriorityRequestCount + openPriorityRequestCount < lastPriorityRequestToCommitCount) {
      revert ZkLighter_CommitBatchPriorityRequestCountMismatch();
    }
    if (
      lastPriorityRequestToCommitCount >= 1 &&
      priorityRequests[lastPriorityRequestToCommitCount - 1].prefixHash != newBatchData.prefixPriorityRequestHash
    ) {
      revert ZkLighter_PriorityRequestPrefixHashMismatch();
    }

    bytes32 aggregatedBlobCommitment = _processBlobs(newBatchData.pubdataCommitments[1:]);

    bytes32 oldStateRoot = lastStoredBatch.stateRoot;
    if (stateRootUpdates[committedBatchesCount] != bytes32(0)) {
      oldStateRoot = stateRootUpdates[committedBatchesCount];
    }

    committedBatchesCount++;
    if (newBatchData.onChainOperationsHash != bytes32(0)) {
      onChainExecutionQueue[executedOnChainBatchesCount + pendingOnChainBatchesCount] = ExecutionQueueItem({
        batchNumber: committedBatchesCount,
        totalPriorityRequests: committedPriorityRequestCount + newBatchData.priorityRequestCount
      });
      pendingOnChainBatchesCount++;
    }

    bytes32 commitment = keccak256(
      abi.encodePacked(
        newBatchData.endBlockNumber,
        newBatchData.batchSize,
        newBatchData.startTimestamp,
        newBatchData.endTimestamp,
        oldStateRoot,
        newBatchData.newStateRoot,
        newBatchData.newValidiumRoot,
        newBatchData.onChainOperationsHash,
        newBatchData.priorityRequestCount,
        newBatchData.prefixPriorityRequestHash,
        aggregatedBlobCommitment
      )
    );
    storedBatchHashes[committedBatchesCount] = hashStoredBatchInfo(
      StoredBatchInfo({
        batchNumber: committedBatchesCount,
        endBlockNumber: newBatchData.endBlockNumber,
        batchSize: newBatchData.batchSize,
        startTimestamp: newBatchData.startTimestamp,
        endTimestamp: newBatchData.endTimestamp,
        priorityRequestCount: newBatchData.priorityRequestCount,
        prefixPriorityRequestHash: newBatchData.prefixPriorityRequestHash,
        onChainOperationsHash: newBatchData.onChainOperationsHash,
        stateRoot: newBatchData.newStateRoot,
        validiumRoot: newBatchData.newValidiumRoot,
        commitment: commitment
      })
    );
    committedPriorityRequestCount += newBatchData.priorityRequestCount;
    emit BatchCommit(committedBatchesCount, newBatchData.batchSize, newBatchData.endBlockNumber);
  }

  /// @inheritdoc IZkLighter
  function verifyBatch(StoredBatchInfo memory batch, bytes calldata proof) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    if (batch.batchNumber != verifiedBatchesCount + 1) {
      revert ZkLighter_VerifyBatchNotInOrder();
    }
    if (hashStoredBatchInfo(batch) != storedBatchHashes[batch.batchNumber]) {
      revert ZkLighter_CannotVerifyNonCommittedBatch();
    }

    uint256[] memory inputs = new uint256[](1);
    inputs[0] = uint256(batch.commitment) % BN254_MODULUS;
    bool success = verifier.Verify(proof, inputs);
    if (!success) {
      revert ZkLighter_VerifyBatchProofFailed();
    }

    emit BatchVerification(batch.batchNumber, batch.batchSize, batch.endBlockNumber);

    verifiedBatchesCount++;
    verifiedPriorityRequestCount += batch.priorityRequestCount;
    lastVerifiedStateRoot = batch.stateRoot;
    lastVerifiedValidiumRoot = batch.validiumRoot;
    lastVerifiedEndBlockNumber = batch.endBlockNumber;
    // Lazy update the executed batches count when:
    // 1. There are no pending items in onChainExecutionQueue and a new batch is verified
    // 2. The next batch in the onChainExecutionQueue is greater than the verifiedBatchesCount
    if (pendingOnChainBatchesCount == 0 || onChainExecutionQueue[executedOnChainBatchesCount].batchNumber > verifiedBatchesCount) {
      executedBatchesCount = verifiedBatchesCount;
      stateRoot = batch.stateRoot;
      validiumRoot = batch.validiumRoot;
      openPriorityRequestCount -= verifiedPriorityRequestCount - executedPriorityRequestCount;
      executedPriorityRequestCount = verifiedPriorityRequestCount;
      emit BatchesExecuted(executedBatchesCount, batch.endBlockNumber);
    }
  }

  function _executeOneBatch(StoredBatchInfo memory batch, bytes memory _onChainOperationsPubData) internal {
    if (storedBatchHashes[batch.batchNumber] != hashStoredBatchInfo(batch)) {
      revert ZkLighter_StoredBatchInfoMismatch();
    }
    bytes32 onChainPubDataHash = bytes32(0);
    uint256 len = _onChainOperationsPubData.length;
    for (uint256 i = 0; i < len; ) {
      uint8 logType;
      (, logType) = Bytes.readUInt8(_onChainOperationsPubData, i);
      if (logType == uint8(TxTypes.OnChainPubDataType.Withdraw)) {
        if (len - i < TxTypes.WithdrawLogSize) {
          revert ZkLighter_InvalidPubDataLength();
        }
        (TxTypes.Withdraw memory _tx, uint256 _offset) = TxTypes.readWithdrawOnChainLog(_onChainOperationsPubData, i);
        increaseBalanceToWithdraw(_tx.masterAccountIndex, _tx.assetIndex, _tx.baseAmount);
        onChainPubDataHash = keccak256(
          abi.encodePacked(onChainPubDataHash, TxTypes.OnChainPubDataType.Withdraw, _tx.masterAccountIndex, _tx.assetIndex, _tx.baseAmount)
        );
        i = _offset;
      } else if (logType == uint8(TxTypes.OnChainPubDataType.USDCWithdraw)) {
        if (len - i < TxTypes.USDCWithdrawLogSize) {
          revert ZkLighter_InvalidPubDataLength();
        }
        (TxTypes.USDCWithdraw memory _tx, uint256 _offset) = TxTypes.readUSDCWithdrawOnChainLog(_onChainOperationsPubData, i);
        increaseBalanceToWithdraw(_tx.masterAccountIndex, USDC_ASSET_INDEX, _tx.usdcAmount);
        onChainPubDataHash = keccak256(
          abi.encodePacked(onChainPubDataHash, TxTypes.OnChainPubDataType.USDCWithdraw, _tx.masterAccountIndex, _tx.usdcAmount)
        );
        i = _offset;
      } else {
        revert ZkLighter_InvalidPubDataType();
      }
    }
    if (onChainPubDataHash != batch.onChainOperationsHash) {
      revert ZkLighter_OnChainOperationsHashMismatch();
    }
  }

  function executeBatches(StoredBatchInfo[] memory batches, bytes[] memory onChainOperationsPubData) external nonReentrant onlyActive {
    if (batches.length != onChainOperationsPubData.length) {
      revert ZkLighter_ExecuteInputLengthMismatch();
    }
    if (batches.length > pendingOnChainBatchesCount) {
      revert ZkLighter_ExecuteInputLengthGreaterThanPendingCount();
    }
    for (uint256 i = 0; i < batches.length; ++i) {
      uint64 batchNumber = batches[i].batchNumber;
      if (batchNumber > verifiedBatchesCount) {
        revert ZkLighter_CannotExecuteNonVerifiedBatch();
      }
      if (batchNumber != onChainExecutionQueue[executedOnChainBatchesCount].batchNumber) {
        revert ZkLighter_BatchNotInOnChainQueue();
      }
      _executeOneBatch(batches[i], onChainOperationsPubData[i]);
      uint64 numExecutedPriorityRequests = onChainExecutionQueue[executedOnChainBatchesCount].totalPriorityRequests - executedPriorityRequestCount;
      executedPriorityRequestCount = onChainExecutionQueue[executedOnChainBatchesCount].totalPriorityRequests;
      executedBatchesCount = batchNumber;
      executedOnChainBatchesCount++;
      pendingOnChainBatchesCount--;
      openPriorityRequestCount -= numExecutedPriorityRequests;
    }
    stateRoot = batches[batches.length - 1].stateRoot;
    validiumRoot = batches[batches.length - 1].validiumRoot;
    // Lazy update the executed batches count when:
    // 1. There are no pending items in onChainExecutionQueue and a new batch is verified
    // 2. The next batch in the onChainExecutionQueue is greater than the verifiedBatchesCount
    if (pendingOnChainBatchesCount == 0 || onChainExecutionQueue[executedOnChainBatchesCount].batchNumber > verifiedBatchesCount) {
      executedBatchesCount = verifiedBatchesCount;
      stateRoot = lastVerifiedStateRoot;
      validiumRoot = lastVerifiedValidiumRoot;
      openPriorityRequestCount -= verifiedPriorityRequestCount - executedPriorityRequestCount;
      executedPriorityRequestCount = verifiedPriorityRequestCount;
      emit BatchesExecuted(executedBatchesCount, lastVerifiedEndBlockNumber);
    } else {
      emit BatchesExecuted(executedBatchesCount, batches[batches.length - 1].endBlockNumber);
    }
  }

  /// @inheritdoc IZkLighter
  function revertBatches(StoredBatchInfo[] memory _batchesToRevert, StoredBatchInfo memory _remainingBatch) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    for (uint32 i = 0; i < _batchesToRevert.length; ++i) {
      StoredBatchInfo memory storedBatchInfo = _batchesToRevert[i];
      if (storedBatchInfo.endBlockNumber == 0) {
        revert ZkLighter_CannotRevertGenesisBatch();
      }
      if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(storedBatchInfo)) {
        revert ZkLighter_StoredBatchInfoMismatch();
      }
      if (storedBatchInfo.batchNumber != committedBatchesCount) {
        revert ZkLighter_StoredBatchInfoMismatch();
      }
      delete storedBatchHashes[committedBatchesCount];
      if (storedBatchInfo.onChainOperationsHash != bytes32(0)) {
        if (pendingOnChainBatchesCount == 0) {
          revert ZkLighter_CannotRevertExecutedBatch();
        }
        uint64 totalOnChainBatchesCount = executedOnChainBatchesCount + pendingOnChainBatchesCount;
        if (onChainExecutionQueue[totalOnChainBatchesCount - 1].batchNumber != storedBatchInfo.batchNumber) {
          revert ZkLighter_StoredBatchInfoMismatch();
        }
        // Remove the batch from the execution queue
        delete onChainExecutionQueue[totalOnChainBatchesCount - 1];
        pendingOnChainBatchesCount--;
      }
      committedBatchesCount--;
      committedPriorityRequestCount -= storedBatchInfo.priorityRequestCount;
      if (storedBatchInfo.batchNumber <= verifiedBatchesCount) {
        verifiedBatchesCount--;
        verifiedPriorityRequestCount -= storedBatchInfo.priorityRequestCount;
      }
    }

    // Can not revert executed batch or priority requests
    if (committedBatchesCount < executedBatchesCount || committedPriorityRequestCount < executedPriorityRequestCount) {
      revert ZkLighter_CannotRevertExecutedBatch();
    }

    // Make sure the remaining batch is the last batch
    if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(_remainingBatch)) {
      revert ZkLighter_StoredBatchInfoMismatch();
    }
    // If we reverted verified batches, update the last verified variables for lazy update on executions
    if (_remainingBatch.batchNumber == verifiedBatchesCount) {
      lastVerifiedStateRoot = _remainingBatch.stateRoot;
      lastVerifiedValidiumRoot = _remainingBatch.validiumRoot;
      lastVerifiedEndBlockNumber = _remainingBatch.endBlockNumber;
    }
    emit BatchesRevert(committedBatchesCount);
  }

  /// @inheritdoc IZkLighter
  function transferERC20(IERC20 _token, address _to, uint256 _amount, uint256 _maxAmount) external returns (uint256) {
    // can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
    if (msg.sender != address(this)) {
      revert ZkLighter_OnlyZkLighter();
    }
    uint256 balanceBefore = _token.balanceOf(address(this));
    SafeERC20.safeTransfer(_token, _to, _amount);
    uint256 balanceAfter = _token.balanceOf(address(this));
    uint256 balanceDiff = balanceBefore - balanceAfter;
    if (balanceDiff > _maxAmount) {
      revert ZkLighter_RollUpBalanceBiggerThanMaxAmount();
    }
    return balanceDiff;
  }

  /// @inheritdoc IZkLighter
  function transferETH(address _to, uint256 _amount) external returns (uint256) {
    // can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
    if (msg.sender != address(this)) {
      revert ZkLighter_OnlyZkLighter();
    }
    (bool success, ) = _to.call{value: _amount}("");
    if (!success) {
      revert ZkLighter_ETHTransferFailed();
    }
    return _amount;
  }

  function increaseBalanceToWithdraw(uint48 _masterAccountIndex, uint16 _assetIndex, uint128 _baseAmount) internal {
    uint128 balance = pendingAssetBalances[_assetIndex][_masterAccountIndex].balanceToWithdraw;
    pendingAssetBalances[_assetIndex][_masterAccountIndex] = PendingBalance(balance + _baseAmount, FILLED_GAS_RESERVE_VALUE);
  }

  /// @inheritdoc IZkLighter
  function getPendingBalance(address _owner, uint16 _assetIndex) external view returns (uint128) {
    uint48 _masterAccountIndex = getAccountIndexFromAddress(_owner);
    return pendingAssetBalances[_assetIndex][_masterAccountIndex].balanceToWithdraw;
  }

  function getPendingBalanceLegacy(address _owner) public view returns (uint128) {
    uint48 _masterAccountIndex = getAccountIndexFromAddress(_owner);
    return DEPRECATED_pendingBalance[_masterAccountIndex].balanceToWithdraw;
  }

  /// @inheritdoc IZkLighter
  function withdrawPendingBalance(address _owner, uint16 _assetIndex, uint128 _baseAmount) external nonReentrant {
    uint48 masterAccountIndex = getAccountIndexFromAddress(_owner);
    uint128 baseBalance = pendingAssetBalances[_assetIndex][masterAccountIndex].balanceToWithdraw;
    if (_baseAmount > baseBalance || _baseAmount == 0) {
      revert ZkLighter_InvalidWithdrawAmount();
    }

    AssetConfig memory assetConfig = assetConfigs[_assetIndex];
    if (_assetIndex != NATIVE_ASSET_INDEX && assetConfig.tokenAddress == address(0)) {
      revert ZkLighter_InvalidAssetIndex();
    }
    uint256 amount = uint256(_baseAmount) * uint256(assetConfig.tickSize);
    uint256 balance = uint256(baseBalance) * uint256(assetConfig.tickSize);
    // We will allow withdrawals of `value` such that:
    // `value` <= user pending balance
    // `value` can be bigger then `_amount` requested if token stakes fee from sender in addition to `_amount` requested
    uint256 transferredAmount;
    if (_assetIndex == NATIVE_ASSET_INDEX) {
      transferredAmount = this.transferETH(_owner, amount);
    } else {
      transferredAmount = this.transferERC20(IERC20(assetConfig.tokenAddress), _owner, amount, balance);
    }
    if (transferredAmount % assetConfig.tickSize != 0) {
      revert ZkLighter_TransferredAmountNotMultipleOfTickSize();
    }

    // update balanceToWithdraw by subtracting the actual amount that was sent
    uint256 transferredBaseAmount = transferredAmount / assetConfig.tickSize;
    uint128 transferredBaseAmount128 = SafeCast.toUint128(transferredBaseAmount);
    pendingAssetBalances[_assetIndex][masterAccountIndex].balanceToWithdraw = baseBalance - transferredBaseAmount128;
    emit WithdrawPending(_owner, _assetIndex, transferredBaseAmount128);
  }

  /// @inheritdoc IZkLighter
  function withdrawPendingBalanceLegacy(address _owner, uint128 _baseAmount) external nonReentrant {
    uint48 masterAccountIndex = getAccountIndexFromAddress(_owner);
    uint128 baseBalance = DEPRECATED_pendingBalance[masterAccountIndex].balanceToWithdraw;
    if (_baseAmount > baseBalance || _baseAmount == 0) {
      revert ZkLighter_InvalidWithdrawAmount();
    }
    // We will allow withdrawals of `value` such that:
    // `value` <= user pending balance
    // `value` can be bigger then `_amount` requested if token takes fee from sender in addition to `_amount` requested
    uint256 amount = this.transferERC20(governance.usdc(), _owner, uint256(_baseAmount), uint256(baseBalance));
    uint128 transferredBaseAmount128 = SafeCast.toUint128(amount);

    // Update balanceToWithdraw by subtracting the actual amount that was sent
    DEPRECATED_pendingBalance[masterAccountIndex].balanceToWithdraw = baseBalance - transferredBaseAmount128;
    emit WithdrawPending(_owner, USDC_ASSET_INDEX, transferredBaseAmount128);
  }

  /// @inheritdoc IZkLighter
  function activateDesertMode() external nonReentrant returns (bool) {
    bool trigger = openPriorityRequestCount != 0 &&
      block.timestamp >= priorityRequests[executedPriorityRequestCount].expirationTimestamp &&
      priorityRequests[executedPriorityRequestCount].expirationTimestamp != 0;
    if (trigger) {
      if (!desertMode) {
        desertMode = true;
        emit DesertMode();
      }
      return true;
    } else {
      return false;
    }
  }

  /// @notice Change address that collects fees from listed markets
  function setTreasury(address _newTreasury) external nonReentrant {
    governance.requireGovernor(msg.sender);
    if (_newTreasury == address(0)) {
      revert ZkLighter_TreasuryCannotBeZero();
    }
    if (getAccountIndexFromAddress(_newTreasury) != NIL_ACCOUNT_INDEX) {
      revert ZkLighter_TreasuryCannotBeInUse();
    }
    treasury = _newTreasury;
    emit TreasuryUpdate(treasury);
  }

  /// @notice Change address that operates the insurance fund
  function setInsuranceFundOperator(address _newInsuranceFundOperator) external nonReentrant {
    governance.requireGovernor(msg.sender);
    if (_newInsuranceFundOperator == address(0)) {
      revert ZkLighter_InsuranceFundOperatorCannotBeZero();
    }
    if (getAccountIndexFromAddress(_newInsuranceFundOperator) != NIL_ACCOUNT_INDEX) {
      revert ZkLighter_InsuranceFundOperatorCannotBeInUse();
    }
    insuranceFundOperator = _newInsuranceFundOperator;
    emit InsuranceFundOperatorUpdate(insuranceFundOperator);
  }

  /// @notice Delegates the call to the additional part of the main contract.
  /// @notice Should be only use to delegate the external calls as it passes the calldata
  /// @notice All functions delegated to additional contract should NOT be nonReentrant
  function delegateAdditional() internal {
    if (address(this) == zklighterImplementation) {
      revert ZkLighter_ImplCantDelegateToAddl();
    }

    address _target = address(additionalZkLighter);
    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, 0x0, calldatasize())
      // Delegatecall method of the implementation contract, returns 0 on error
      let result := delegatecall(gas(), _target, ptr, calldatasize(), 0x0, 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, 0x0, size)

      // Depending on 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)
      }
    }
  }

  /// @notice Calls the point evaluation precompile to verify the blob commitment
  /// @param blobVersionedHash is the versioned hash of the blob
  /// @param evaluationPointCommitmentProof is the evaluation point commitment proof
  function _pointEvaluationPrecompile(bytes32 blobVersionedHash, bytes calldata evaluationPointCommitmentProof) internal view {
    bytes memory precompileInput = abi.encodePacked(blobVersionedHash, evaluationPointCommitmentProof);

    (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDRESS.staticcall(precompileInput);

    // We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the
    // response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile
    if (!success) {
      revert ZkLighter_InvalidPointEvaluationParams();
    }
    (, uint256 result) = abi.decode(data, (uint256, uint256));
    if (result != BLS_MODULUS) {
      revert ZkLighter_InvalidPointEvaluationParams();
    }
  }

  /// @notice Verifies if the sent blob commitment data is consistent with the blob itself
  /// @param pubDataCommitments is a list of: evaluationPointX (32 bytes) || evaluationPointY (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 160 bytes
  /// @return aggregatedBlobCommitment is the aggregated blob commitment for all blobs in the pubDataCommitments
  function _processBlobs(bytes calldata pubDataCommitments) internal view returns (bytes32 aggregatedBlobCommitment) {
    if (pubDataCommitments.length % BLOB_DATA_COMMITMENT_BYTE_SIZE != 0) {
      revert ZkLighter_InvalidBlobCommitmentParams();
    }

    uint256 blobCount = pubDataCommitments.length / BLOB_DATA_COMMITMENT_BYTE_SIZE;
    if (blobCount > MAX_BLOB_COUNT) {
      revert ZkLighter_InvalidBlobCount(blobCount);
    }

    for (uint256 i = 0; i < blobCount; i++) {
      bytes calldata _blobDataCommitment = pubDataCommitments[i * BLOB_DATA_COMMITMENT_BYTE_SIZE:(1 + i) * BLOB_DATA_COMMITMENT_BYTE_SIZE];
      bytes32 evaluationPointX = bytes32(_blobDataCommitment[0:32]);
      bytes32 evaluationPointY = bytes32(_blobDataCommitment[32:64]);
      bytes32 blobVersionedHash;
      assembly {
        blobVersionedHash := blobhash(i)
      }
      if (blobVersionedHash == bytes32(0)) {
        revert ZkLighter_InvalidBlobCommitmentParams();
      }

      _pointEvaluationPrecompile(blobVersionedHash, _blobDataCommitment);

      bytes32 currentBlobCommitment = keccak256(abi.encodePacked(evaluationPointX, evaluationPointY, blobVersionedHash));
      if (i == 0) {
        aggregatedBlobCommitment = currentBlobCommitment;
      } else {
        aggregatedBlobCommitment = keccak256(abi.encodePacked(aggregatedBlobCommitment, currentBlobCommitment));
      }
    }

    // Verify there are no extra blob hashes attached to the call. `blobhash` will return 0 bytes if no blob
    // hash is present at given index. Leaps are not allowed, so checking the first next index is sufficient.
    bytes32 emptyBlobVersionedHash;
    assembly {
      emptyBlobVersionedHash := blobhash(blobCount)
    }
    if (emptyBlobVersionedHash != bytes32(0)) {
      revert ZkLighter_InvalidBlobCommitmentParams();
    }

    return aggregatedBlobCommitment;
  }
}

Read Contract

MAX_ACCOUNT_INDEX 0x437545f9 → uint48
MAX_API_KEY_INDEX 0xda16ec10 → uint8
MAX_ASSET_INDEX 0x26012922 → uint16
MAX_BATCH_DEPOSIT_LENGTH 0xba279a49 → uint64
MAX_DEPOSIT_CAP_TICKS 0x6883bde6 → uint64
MAX_MASTER_ACCOUNT_INDEX 0xb60cbbd7 → uint48
MAX_ORDER_BASE_AMOUNT 0xa2914196 → uint48
MAX_ORDER_PRICE 0x0b9a9eb0 → uint32
MAX_ORDER_QUOTE_AMOUNT 0xf22b50ff → uint48
MAX_PERPS_MARKET_INDEX 0xa1922dfc → uint16
MAX_POOL_SHARES_TO_MINT_OR_BURN 0x8542fb14 → uint64
MAX_SPOT_MARKET_INDEX 0x79c929c1 → uint16
MAX_STAKING_SHARES_TO_MINT_OR_BURN 0x93371c1a → uint64
MAX_TICK_SIZE 0xb1e09d45 → uint128
MIN_ASSET_INDEX 0x9d5aeec5 → uint16
MIN_ORDER_BASE_AMOUNT 0x6997fb5e → uint48
MIN_ORDER_PRICE 0x706d6fbe → uint32
MIN_POOL_SHARES_TO_MINT_OR_BURN 0x4ae71a21 → uint64
MIN_SPOT_MARKET_INDEX 0xff6b936c → uint16
MIN_STAKING_SHARES_TO_MINT_OR_BURN 0xf051e760 → uint64
NATIVE_ASSET_INDEX 0xbfda3066 → uint16
NIL_ACCOUNT_INDEX 0x348e8a93 → uint48
NIL_ORDER_BASE_AMOUNT 0xe8b2f939 → uint48
PRIORITY_EXPIRATION 0x71175ff9 → uint256
USDC_ASSET_INDEX 0x7de213eb → uint16
__DEPRECATED_stateRootUpdates 0x30f72b0a → bytes32
addressToAccountIndex 0xabf6a038 → uint48
assetConfigs 0xcd565e08 → address, uint8, uint56, uint128, uint64, uint64
committedBatchesCount 0xaab78a8e → uint64
committedPriorityRequestCount 0xdb085664 → uint64
desertMode 0x02cfb563 → bool
executedBatchesCount 0xd5102eea → uint64
executedOnChainBatchesCount 0x1b06a763 → uint64
executedPriorityRequestCount 0x634f0262 → uint64
getPendingBalance 0xd1cbc64f → uint128
getPendingBalanceLegacy 0xaf7c0260 → uint128
insuranceFundOperator 0xbaa08f7d → address
lastAccountIndex 0xfae2e046 → uint48
lastVerifiedEndBlockNumber 0xe64d8795 → uint64
lastVerifiedStateRoot 0x3177a48e → bytes32
lastVerifiedValidiumRoot 0x3eec9538 → bytes32
openPriorityRequestCount 0x72fc4c39 → uint64
pendingOnChainBatchesCount 0x31c5da4e → uint64
stateRoot 0x9588eca2 → bytes32
stateRootUpdates 0xe9560055 → bytes32
storedBatchHashes 0xa44a9316 → bytes32
tokenToAssetIndex 0x899cfa29 → uint16
treasury 0x61d027b3 → address
validiumRoot 0x45dca05c → bytes32
verifiedBatchesCount 0x90fda39b → uint64
verifiedPriorityRequestCount 0x64420e05 → uint64

Write Contract 30 functions

These functions modify contract state and require a wallet transaction to execute.

activateDesertMode 0x22b22256
No parameters
returns: bool
burnShares 0xae150b39
uint48 _accountIndex
uint48 _publicPoolIndex
uint64 _shareAmount
cancelAllOrders 0xa4b6f756
uint48 _accountIndex
cancelOutstandingDepositsForDesertMode 0x1b6592fa
uint64 _n
bytes[] _priorityPubData
changePubKey 0x17010c68
uint48 _accountIndex
uint8 _apiKeyIndex
bytes _pubKey
commitBatch 0x9f53dbe4
tuple newBatchData
tuple lastStoredBatch
createMarket 0x90cdef08
uint8 _size_decimals
uint8 _price_decimals
bytes32 _symbol
tuple _params
createOrder 0x3c40c676
uint48 _accountIndex
uint16 _marketIndex
uint48 _baseAmount
uint32 _price
uint8 _isAsk
uint8 _orderType
deposit 0x8a857083
address _to
uint16 _assetIndex
uint8 _routeType
uint256 _amount
depositBatch 0xea2102e4
uint64[] _amount
address[] _to
uint48[] _accountIndex
executeBatches 0xcb24261e
tuple[] batches
bytes[] onChainOperationsPubData
initialize 0x439fab91
bytes initializationParameters
performDesert 0xc157483d
uint48 _accountIndex
uint48 _masterAccountIndex
uint16 _assetIndex
uint128 _totalBaseAmount
bytes proof
registerAsset 0x0884f5ba
uint8 _l1Decimals
uint8 _decimals
bytes32 _symbol
tuple _params
registerAssetConfig 0x25216fda
uint16 assetIndex
address tokenAddress
uint8 withdrawalsEnabled
uint56 extensionMultiplier
uint128 tickSize
uint64 depositCapTicks
uint64 minDepositTicks
revertBatches 0xecb21edc
tuple[] _batchesToRevert
tuple _remainingBatch
setInsuranceFundOperator 0xaabe6078
address _newInsuranceFundOperator
setSystemConfig 0x75b97281
tuple _params
setTreasury 0xf0f44260
address _newTreasury
transferERC20 0x55a2ba68
address _token
address _to
uint256 _amount
uint256 _maxAmount
returns: uint256
transferETH 0x7b1a4909
address _to
uint256 _amount
returns: uint256
updateAsset 0x87b1be37
tuple _params
updateAssetConfig 0xcd626497
uint16 assetIndex
uint8 withdrawalsEnabled
uint64 depositCapTicks
uint64 minDepositTicks
updateMarket 0xed51285e
tuple _params
updateStateRoot 0x3b2aa09b
tuple _lastStoredBatch
bytes32 _stateRoot
bytes32 _validiumRoot
bytes proof
upgrade 0x25394645
bytes upgradeParameters
verifyBatch 0x87066081
tuple batch
bytes proof
withdraw 0xd20191bd
uint48 _accountIndex
uint16 _assetIndex
uint8 _routeType
uint64 _baseAmount
withdrawPendingBalance 0x2f25807e
address _owner
uint16 _assetIndex
uint128 _baseAmount
withdrawPendingBalanceLegacy 0x975364c6
address _owner
uint128 _baseAmount

Recent Transactions

No transactions found for this address