Address Contract Verified
Address
0x803bd6a0577c083c1EDe82Da455C8e69e697c878
Balance
0 ETH
Nonce
1
Code Size
24480 bytes
Creator
0xfDb36C13...4A41 at tx 0x47457fe9...30d967
Indexed Transactions
0
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