Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x927A83c679A5e1a6435D6BfaEF7F20D4db23e2cC
Balance 0 ETH
Nonce 1
Code Size 19550 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

19550 bytes
0x60806040526004361015610022575b3615610018575f80fd5b610020612b79565b005b5f3560e01c806301d523b61461035157806301e1d1141461034c5780630402f19614610347578063066055e01461034257806307a2d13a1461033d57806318f72950146103385780631913bb24146103335780631a7ff5531461032e578063201b9eb51461032957806324a14b14146103245780632999ad3f1461031f5780632cdf74011461031a5780633229fa951461031557806333194c0a1461031057806336fe59d21461030b5780633a98ef39146103065780633e1655d314610301578063439fab91146102fc57806343e82a79146102f757806346904840146102f25780634ec96b22146102ed5780634f1ef286146102e857806352d1902d146102e357806353156f28146102de57806354fd4d50146102d95780635c0935db146102d45780635c60da1b146102cf5780635cfc1a51146102ca57806360d60e6e146102c5578063704b6c02146102c057806372b410a8146102bb578063754c3888146102b657806376b58b90146102b1578063776ae2b0146102ac57806378f61ca8146102a75780637fd6f15c146102a257806383d430d51461029d5780638697d2c2146102985780638ceab9aa146102935780639267842a1461028e578063a145ca0114610289578063a49a1e7d14610284578063ac9650d81461027f578063ad3cb1cc1461027a578063b1f0e7c714610275578063c6e6f59214610270578063d40a902f1461026b578063e74b981b14610266578063f04da65b14610261578063f851a4401461025c5763f9609f080361000e576122de565b6122b7565b61227c565b61224f565b61218a565b61216c565b612144565b6120fd565b612091565b611fbc565b611f29565b611cd7565b611ca3565b611b11565b61199d565b611979565b6118e3565b6118b4565b611864565b6117e3565b61174b565b61171e565b61167b565b611661565b61162d565b611468565b61144d565b611429565b6113c3565b61114b565b610fe0565b610fb8565b610ea9565b610dc7565b610d1c565b610cf6565b610cdc565b610ca2565b610c76565b610c5c565b610898565b610852565b610838565b6107d9565b610763565b61070f565b6106f1565b610550565b61042d565b6103fe565b610385565b6001600160a01b031690565b6001600160a01b0381160361037357565b5f80fd5b908160809103126103735790565b60803660031901126103735760043561039d81610362565b6024356044356103ac81610362565b606435916001600160401b038311610373576020936103da6103d56103ec953690600401610377565b612419565b6103e58334336141da565b503361347b565b604051908152f35b5f91031261037357565b34610373575f36600319011261037357602060985460801c604051908152f35b359061ffff8216820361037357565b346103735760203660031901126103735760043561ffff811680820361037357610455612b83565b61045d613e2f565b612710811161051b5760655461047f60b082901c6001600160401b03166123e1565b42106105325760a01c61ffff16801561052a5761049e6104a591612b96565b6064900490565b1061051b576065805467ffffffffffffffff60b01b4260b01b1669ffffffffffffffffffff60a01b1990911661ffff60a01b60a085901b161717905560405161ffff909116815233907f96e06190cc0894376ae1c3ba56586573ed2786a831b68ad88c6a0410d33163049080602081015b0390a2005b638a81d3b360e01b5f5260045ffd5b5060646104a5565b612d2360f21b5f5260045ffd5b6001600160801b0381160361037357565b346103735760203660031901126103735760043561056d8161053f565b604051633b9e9f0160e21b81523360048201526001600160801b03821660248201526020816044815f6001600160a01b037f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306165af19081156106ec575f916106bd575b50335f908152610137602052604090206105e990612322565b916001600160801b0361060384516001600160801b031690565b16156106ae5761065c836106196106aa95612bcf565b6106436106368461063184516001600160801b031690565b61235b565b6001600160801b03168252565b335f9081526101376020526040902061237b565b61237b565b604080518381526001600160801b0392909216602083015233917f3f7354ba02880b4fa37a629985852a38417ff369369ce1e52fa6f8342a9100a79190a26040519081529081906020820190565b0390f35b63673f032f60e11b5f5260045ffd5b6106df915060203d6020116106e5575b6106d781836110b9565b810190612308565b5f6105d0565b503d6106cd565b612317565b346103735760203660031901126103735760206103ec6004356123ad565b60603660031901126103735760043561072781610362565b60243561073381610362565b604435906001600160401b0382116103735760209261075c6103d56103ec943690600401610377565b34906141da565b5f36600319011261037357610776612e17565b34156107ca5760a0543481018091116107c55760a055604080513381523460208201527f0e51de435f58267ce69726383d596a857c4be319b0cf09c61f3f1e99804d8d0e91819081015b0390a1005b612347565b6318374fd160e21b5f5260045ffd5b34610373576020366003190112610373576004356001600160401b038111610373576103d5610020913690600401610377565b60609060031901126103735760043561082481610362565b906024359060443561083581610362565b90565b346103735760206103ec61084b3661080c565b913361347b565b34610373575f36600319011261037357602060d554604051908152f35b6060906003190112610373576004359060243561088b81610362565b9060443561083581610362565b34610373576108a63661086f565b906001600160a01b03821615610c4d576108be613e2f565b7f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03811691823b1561037357604051631d8557d760e01b81525f8160048183885af180156106ec57610c33575b506001600160a01b0381165f9081526101376020526040902061093490612322565b6001600160801b0361094d82516001600160801b031690565b16156106ae5761095c81612bcf565b61099b602061097283516001600160801b031690565b6040516303d1689d60e11b81526001600160801b03909116600482015291829081906024820190565b0381885afa80156106ec57610a85946020925f92610c14575b506001600160a01b0385165f908152609c602052604090206109d7905b546123ad565b6109df61246a565b906109e86110da565b93845284840152604083018a90526060830152600160808301525b60408051636f92a36f60e11b81526001600160a01b037f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb5981166004830152909216602483015282516044830152602083015160648301528201516084820152606082015160a4820152608090910151151560c4820152938490819060e4820190565b038173b3ce222b28fda660c1375246a179d2acce4a0f745af49283156106ec575f93610bf3575b50604051633b9e9f0160e21b81523360048201526024810187905293602090859060449082905f905af19081156106ec577f61fd285f9e34a3dbfa9846bdcf22a023e37a3c93549902843b30dd74a18c535094610b4592610bd6575b50610b28610636610b1889613fc4565b83516001600160801b031661235b565b6001600160a01b0383165f9081526101376020526040902061237b565b610bd1610b5183614041565b92610b87610b6c610b6183613fc4565b60985460801c61235b565b6001600160801b036098549181199060801b16911617609855565b610b918484614577565b610b9b8187613dd4565b604080516001600160a01b0397881681526020810198909852870193909352606086019290925290921692339281906080820190565b0390a3005b610bee9060203d6020116106e5576106d781836110b9565b610b08565b610c0d91935060203d6020116106e5576106d781836110b9565b915f610aac565b610c2c919250833d85116106e5576106d781836110b9565b905f6109b4565b80610c415f610c47936110b9565b806103f4565b5f610912565b63d92e233d60e01b5f5260045ffd5b34610373575f3660031901126103735760206103ec61246a565b34610373575f366003190112610373576020610c906124a6565b6040516001600160a01b039091168152f35b34610373575f3660031901126103735760206040517fd92dbcef7ed61a67c0eefa7cafcc41f41d9402a5046486977364b4724c821f8b8152f35b60206103ec610cea3661080c565b916103e58334336141da565b34610373575f3660031901126103735760206001600160801b0360985416604051908152f35b34610373575f3660031901126103735760a0609954609e546001600160801b03610d4461398b565b9160405193828116855260801c60208501528060801c60408501521660608301526080820152f35b9181601f84011215610373578235916001600160401b038311610373576020838186019501011161037357565b602060031982011261037357600435906001600160401b03821161037357610dc391600401610d6c565b9091565b610dd036610d99565b5f80516020614c32833981519152549160ff8360401c168015610e95575b610e865768ffffffffffffffffff199290921668010000000000000005175f80516020614c3283398151915255610e2491612577565b610e5368ff0000000000000000195f80516020614c3283398151915254165f80516020614c3283398151915255565b604051600581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081016107c0565b63f92ee8a960e01b5f5260045ffd5b5060056001600160401b0384161015610dee565b3461037357610eb73661086f565b60405163057453a760e31b81529091906020816004817f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb596001600160a01b03165afa9081156106ec575f91610f89575b506001600160a01b03163303610f7a57610bd181610f4785857f57f5eb636bf62215c111b54545422f11dfb0cb115f606be905f0be08e8859dd395613725565b604080516001600160a01b0397881681526020810198909852870191909152606086015290921692339281906080820190565b634ca8886760e01b5f5260045ffd5b610fab915060203d602011610fb1575b610fa381836110b9565b8101906124e1565b5f610f07565b503d610f99565b34610373575f366003190112610373576065546040516001600160a01b039091168152602090f35b3461037357602036600319011261037357600435610ffd81610362565b60018060a01b03165f526101376020526106aa6001600160801b0360405f20604051906110298261107e565b548281169081835260801c602083015261105c575b5116604051918291829190916001600160801b036020820193169052565b61106581612bcf565b61103e565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b0382111761109957604052565b61106a565b606081019081106001600160401b0382111761109957604052565b90601f801991011681019081106001600160401b0382111761109957604052565b604051906110e960a0836110b9565b565b604051906110e96040836110b9565b6001600160401b03811161109957601f01601f191660200190565b929192611121826110fa565b9161112f60405193846110b9565b829481845281830111610373578281602093845f960137010152565b60403660031901126103735760043561116381610362565b6024356001600160401b0381116103735736602382011215610373576111d36111996111c5923690602481600401359101611115565b6111a1613b34565b60405192839163439fab9160e01b602084015260206024840152604483019061200e565b03601f1981018352826110b9565b6111db613b34565b6111e3612b83565b6001600160a01b0382168015908115611396575b8115611326575b81156112c0575b508015611229575b61121a576100209161479c565b6355299b4960e01b5f5260045ffd5b506040516345da87c560e01b81526001600160a01b0383166004820152602081806024810103817f0000000000000000000000003a0008a588772446f6e656133c2d5029cc4fc20e6001600160a01b03165afa9081156106ec575f91611291575b501561120d565b6112b3915060203d6020116112b9575b6112ab81836110b9565b8101906126df565b5f61128a565b503d6112a1565b60405163054fd4d560e41b81529150602090829060049082905afa9081156106ec575f916112f7575b5060ff16600614155f611205565b611319915060203d60201161131f575b61131181836110b9565b810190614783565b5f6112e9565b503d611307565b60405163198ca60560e11b8152909150602081600481855afa80156106ec577fd92dbcef7ed61a67c0eefa7cafcc41f41d9402a5046486977364b4724c821f8b915f91611377575b501415906111fe565b611390915060203d6020116106e5576106d781836110b9565b5f61136e565b5f80516020614bf28339815191525490915081906113bc906001600160a01b0316610356565b14906111f7565b34610373575f366003190112610373577f000000000000000000000000927a83c679a5e1a6435d6bfaef7f20d4db23e2cc6001600160a01b0316300361141a5760206040515f80516020614bf28339815191528152f35b63703e46dd60e11b5f5260045ffd5b5f366003190112610373576001600160a01b036114446124a6565b163303610f7a57005b34610373575f36600319011261037357602060405160058152f35b6060366003190112610373576004356001600160401b03811161037357611493903690600401610d6c565b6024356001600160401b038111610373576114b2903690600401610d6c565b6044939193356001600160401b038111610373576114f76114da6114fb923690600401610d6c565b9390966114e5613b92565b6114ed612e17565b60d5548787613c0b565b1590565b610f7a575f93816115a3575b5050732319ace2793561520fb600da1c4b09a660a4adb5803b156103735761156a935f936040519586948593849363163be54b60e01b85527f0000000000000000000000000000bbddc7ce488642fb579f8b00f3a590007251926004860161268e565b03915af480156106ec5761158f575b61002060015f80516020614c1283398151915255565b80610c415f61159d936110b9565b5f611579565b9193507f000000000000000000000000033e5bae5bdc459cbb7d388b41a9d62020be810f6001600160a01b03169190823b15610373576115ff925f92836040518096819582946301bb2b5360e71b84528a8c306004870161265a565b03925af180156106ec57611619575b506001915f80611507565b80610c415f611627936110b9565b5f61160e565b34610373575f366003190112610373575f80516020614bf2833981519152546040516001600160a01b039091168152602090f35b34610373575f3660031901126103735760206103ec6126c4565b346103735760203660031901126103735760043560405190633ec7cf4b60e11b8252609a6004830152602482015260208160448173723399c5a3503a53af1bdf89bda7d6d1025cab975af49081156106ec575f916116ff575b50609a548110156116f4576106aa905b6040519081529081906020820190565b506106aa5f196116e4565b611718915060203d6020116106e5576106d781836110b9565b5f6116d4565b346103735760203660031901126103735761002060043561173e81610362565b611746612b83565b613d0c565b34610373575f36600319011261037357604051633eb1acf760e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa80156106ec576106aa915f916117c4575b5060405190151581529081906020820190565b6117dd915060203d6020116112b9576112ab81836110b9565b5f6117b1565b346103735760203660031901126103735760043561180081610362565b611808612b83565b60d2546001600160a01b039182169181168214611855576001600160a01b031916811760d255337f6bdc78d8c88160b3fc3638e67f2afe523b3f4c7d00c56ebb6216790e4c3eb2cb5f80a3005b638c8728c760e01b5f5260045ffd5b34610373576080366003190112610373576106aa61189760043561188781610362565b6024356044359060643592612716565b604080519384526020840192909252908201529081906060820190565b34610373576020366003190112610373576004355f5260d4602052602060ff60405f2054166040519015158152f35b34610373575f366003190112610373576118fb612b83565b611903613d65565b61196a574760985460801c81039081116107c55761191f613b92565b804710611954575f80808093335af1611936613ff5565b901561194f5760015f80516020614c1283398151915255005b614bd3565b4763cf47918160e01b5f5260045260245260445ffd5b6389a1dc6360e01b5f5260045ffd5b34610373575f36600319011261037357602061ffff60655460a01c16604051908152f35b34610373576040366003190112610373576004356001600160401b03811161037357806004019060a06003198236030112610373576024356001600160401b038111610373576119f1903690600401610d6c565b9091907f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b031690813b15610373575f604051809363837d444160e01b8252818381611a468b60048301612848565b03925af180156106ec57611a7d936114f793604492611afd575b50611a69613e2f565b0193611a7585876128c5565b873591613c0b565b610f7a57611a8f5f91611aab936128c5565b604051630ee0f42760e21b815293849283929060048401612a7e565b0381732319ace2793561520fb600da1c4b09a660a4adb55af480156106ec57610020915f91611adb575b50613ef1565b611af791503d805f833e611aef81836110b9565b810190612954565b5f611ad5565b80610c415f611b0b936110b9565b5f611a60565b3461037357606036600319011261037357600435602435611b36604435828433612716565b9192611b627f000000000000000000000000000000000000000000000000000000000000d2f0826123f1565b42108015611c9b575b8015611c93575b611c84577feb3b05c070c24f667611fdb3ff75fe007d42401c573aed8d8faca95fd00ccb5693611bcd611bb2611ba786613fc4565b60995460801c61235b565b6001600160801b036099549181199060801b16911617609955565b604080513360208201908152918101849052606081018890525f91611c0a91611bf981608081016111c5565b5190205f52609b60205260405f2090565b555f9260018211611c41575b505050611c238233613dd4565b60408051948552602085019190915283015233918060608101610516565b611c7b91929350611c5290876123f1565b60408051336020820190815291810195909552606085018290529093611bf981608081016111c5565b555f8080611c16565b630e3d8e8d60e11b5f5260045ffd5b508215611b72565b508115611b6b565b34610373576040366003190112610373576020611cce602435600435611cc882610362565b336149c6565b6103ec33614934565b3461037357602036600319011261037357600435611cf3613e2f565b335f90815261013760205260409020611d0b90612322565b6001600160801b03611d2482516001600160801b031690565b16156106ae57611d3381612bcf565b816001600160801b03611d4d83516001600160801b031690565b1610611ece57335f908152609c602052604090205482611d83611d7784516001600160801b031690565b6001600160801b031690565b14611eb857611da79083611da1611d7785516001600160801b031690565b91612d8c565b91611ddc611dcf611db783613fc4565b84516001600160801b0316036001600160801b031690565b6001600160801b03168352565b335f90815261013760205260409020611df690839061237b565b611e3e6020611e2f60018060a01b037f00000000000000000000000009e84205df7c68907e619d07afd90143c5763605168096336149c6565b9301516001600160801b031690565b833b156103735760405163074ee96960e31b81523360048201526024810184905260448101929092526001600160801b03166064820152915f908390608490829084905af19182156106ec576106aa92611ea4575b506040519081529081906020820190565b80610c415f611eb2936110b9565b5f611e93565b335f908152610137602052604081205591611df6565b636edcc52360e01b5f5260045ffd5b6040600319820112610373576004356001600160401b0381116103735781611f0791600401610d6c565b92909291602435906001600160401b03821161037357610dc391600401610d6c565b3461037357611f50611f3a36611edd565b90611f46949294613e2f565b60d5548585613c0b565b15610f7a57611f8a5f91604093600185519586948594630ee0f42760e21b8652600486019460d4865260606020870152606086019161263a565b9201520381732319ace2793561520fb600da1c4b09a660a4adb55af480156106ec57610020915f91611adb5750613ef1565b34610373576105167f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf611fee36610d99565b9290611ff8612b83565b604051918291602083523395602084019161263a565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b83831061206457505050505090565b9091929394602080612082600193603f19868203018752895161200e565b97019301930191939290612055565b34610373576020366003190112610373576004356001600160401b0381116103735736602382011215610373578060040135906001600160401b038211610373573660248360051b83010111610373576106aa9160246120f19201612ae6565b60405191829182612032565b34610373575f366003190112610373576106aa60405161211e6040826110b9565b60058152640352e302e360dc1b602082015260405191829160208352602083019061200e565b34610373575f3660031901126103735760d2546040516001600160a01b039091168152602090f35b346103735760203660031901126103735760206103ec600435614041565b6121aa61219636611edd565b906121a2949294613b92565b611f46612e17565b15610f7a57732319ace2793561520fb600da1c4b09a660a4adb590813b15610373575f916121f891604051809581948293632dc6eba360e11b8452600484019260408452604084019161263a565b7f00000000000000000000000000000961ef480eb55e80d19ad83579a64c0070026001600160a01b031660209092019190915203915af480156106ec5761158f5761002060015f80516020614c1283398151915255565b346103735760203660031901126103735761002060043561226f81610362565b612277612b83565b6140c6565b346103735760203660031901126103735760043561229981610362565b60018060a01b03165f52609c602052602060405f2054604051908152f35b34610373575f366003190112610373575f546040516001600160a01b039091168152602090f35b60403660031901126103735760206103ec6004356122fb81610362565b6024359061075c82610362565b90816020910312610373575190565b6040513d5f823e3d90fd5b9060405161232f8161107e565b91546001600160801b038116835260801c6020830152565b634e487b7160e01b5f52601160045260245ffd5b906001600160801b03809116911603906001600160801b0382116107c557565b815160209092015160801b6fffffffffffffffffffffffffffffffff19166001600160801b0392909216919091179055565b6098546001600160801b03811690816123c557505090565b916108359260801c90612d8c565b90600182018092116107c557565b906203f48082018092116107c557565b919082018092116107c557565b9190915f83820193841291129080158216911516176107c557565b61242561243491612f00565b91908083612445575b506130a0565b61243a57565b612442613274565b50565b60a0549081612455575b5061242e565b61246392505f60a0556123fe565b5f8061244f565b476099546124806001600160801b0382166123ad565b6001600160801b03609e5416019060801c01908181115f146124a0570390565b50505f90565b61016a546001600160a01b031680156124bc5790565b507f00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff8690565b90816020910312610373575161083581610362565b602081830312610373578035906001600160401b038211610373570190606082820312610373576040519161252a8361109e565b8035835261253a6020820161041e565b60208401526040810135906001600160401b03821161037357019080601f830112156103735781602061256f93359101611115565b604082015290565b5f549091906001600160a01b03166126305760405163e7f6f22560e01b815290602082600481335afa9182156106ec575f9261260f575b50604051636f4fa30f60e01b815292602084600481335afa9081156106ec576110e9945f926125ec575b506125e691928101906124f6565b916139f7565b6125e692506126099060203d602011610fb157610fa381836110b9565b916125d8565b61262991925060203d602011610fb157610fa381836110b9565b905f6125ae565b50506110e96145c3565b908060209392818452848401375f828201840152601f01601f1916010190565b939161083595936126809260018060a01b0316865260606020870152606086019161263a565b92604081850391015261263a565b929493906126ae9060609360d4865260806020870152608086019161263a565b94151560408401526001600160a01b0316910152565b609d548061083557505f1990565b5190811515820361037357565b9081602091031261037357610835906126d2565b9190826040910312610373576020825192015190565b919082039182116107c557565b604080516001600160a01b03909216602083019081529082019390935260608101829052909161274d91611bf981608081016111c5565b5491821561280c576040805163776e861f60e01b8152609a6004820152602481019290925260448201929092526064810183905292908360848173723399c5a3503a53af1bdf89bda7d6d1025cab975af480156106ec575f935f916127d7575b50906127ba848094612709565b93600185146127c65750565b935091506127d45f936123d3565b91565b90506127fc91935060403d604011612805575b6127f481836110b9565b8101906126f3565b9290925f6127ad565b503d6127ea565b5050505f905f905f90565b9035601e19823603018112156103735701602081359101916001600160401b03821161037357813603831361037357565b90610835916020815281356020820152602082013560408201526128b36128a86128896128786040860186612817565b60a0606087015260c086019161263a565b6128966060860186612817565b858303601f190160808701529061263a565b926080810190612817565b9160a0601f198286030191015261263a565b903590601e198136030182121561037357018035906001600160401b0382116103735760200191813603831361037357565b6001600160401b0381116110995760051b60200190565b81601f8201121561037357805190612925826110fa565b9261293360405194856110b9565b8284526020838301011161037357815f9260208093018386015e8301015290565b602081830312610373578051906001600160401b038211610373570181601f8201121561037357805190612987826128f7565b9261299560405194856110b9565b82845260208085019360051b830101918183116103735760208101935b8385106129c157505050505090565b84516001600160401b03811161037357820160a0818503601f190112610373576129e96110da565b9160208201516001600160401b03811161037357856020612a0c9285010161290e565b835260408201516001600160401b03811161037357856020612a309285010161290e565b60208401526060820151926001600160401b0384116103735760a083612a5d88602080988198010161290e565b604084015260808101516060840152015160808201528152019401936129b2565b9291612a9d5f9260409260d4875260606020880152606087019161263a565b930152565b634e487b7160e01b5f52603260045260245ffd5b90821015612acd57610dc39160051b8101906128c5565b612aa2565b8051821015612acd5760209160051b010190565b919091612af2836128f7565b612aff60405191826110b9565b838152601f19612b0e856128f7565b015f5b818110612b6857505080935f5b818110612b2b5750505050565b80612b4c612b46612b3f6001948689612ab6565b3691611115565b30614024565b612b568286612ad2565b52612b618185612ad2565b5001612b1e565b806060602080938601015201612b11565b6124423433614135565b5f546001600160a01b03163303610f7a57565b906078820291808304607814901517156107c557565b634e487b7160e01b5f52601260045260245ffd5b8115612bca570490565b612bac565b60405163752a536d60e01b8152906020826004817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165afa9182156106ec575f92612c92575b506020810190612c3482516001600160801b031690565b926001600160801b0384168114612c8c57612c7a82612c7f612c7a6110e997856001600160801b0380612c71612c7f9a516001600160801b031690565b16921691612d8c565b613fc4565b6001600160801b03169052565b50505050565b612cac91925060203d6020116106e5576106d781836110b9565b905f612c1d565b612cbd8282614282565b918115612d0f57816127101115612d08577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e9193612710910990828211900360fc1b910360041c170290565b6011614295565b506127109250500490565b612d248282614282565b918115612d7b5781670de0b6b3a76400001115612d08577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066993670de0b6b3a7640000910990828211900360ee1b910360121c170290565b50670de0b6b3a76400009250500490565b91612d978284614282565b9290938415612e0a5784831115612dfd5790829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b6011600384150218614295565b5050906108359250612bc0565b612e1f613d65565b15612e2657565b630a62fbdb60e11b5f5260045ffd5b90816060910312610373578051916108356040602084015193016126d2565b81835290916001600160fb1b0383116103735760209260051b809284830137010190565b90602082528035602083015260208101358060130b8091036103735760408301526040810135612ea781610362565b6001600160a01b031660608381019190915281013536829003601e19018112156103735701602081359101906001600160401b038111610373578060051b360382136103735760a0836080806108359601520191612e54565b906060612f2192604051809481926325f56f1160e01b835260048301612e78565b03815f7f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165af19081156106ec575f925f905f9361305a575b508284811561305357506001600160a01b039050612f7e6124a6565b167f00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff866001600160a01b03168103612ffd5781612fb9575b5050565b803b1561037357604051636ee3193160e11b815260048101929092525f908290602490829084905af180156106ec57612fef5750565b80610c415f6110e9936110b9565b5f92949150602090600460405180958193634641257d60e01b83525af19081156106ec576127d4925f92613032575b506123fe565b61304c91925060203d6020116106e5576106d781836110b9565b905f61302c565b9450925050565b91935050613080915060603d606011613089575b61307881836110b9565b810190612e35565b9192905f612f62565b503d61306e565b600160ff1b81146107c5575f0390565b8015612442576130b5611d7760985460801c90565b5f82126131a357816130c6916123f1565b906130d3610b6c83613fc4565b6130f36065549161ffff6130ec8461ffff9060a01c1690565b1690612cb3565b801561319e577f555ee6b2ef9506d870f386c067e47d3689435330b012ad263d8cc353186865479281613131611d776098546001600160801b031690565b918261317d57509261317891506001600160a01b03165b9161315384846142a5565b60405193849384604091949392606082019560018060a01b0316825260208201520152565b0390a1565b613178926131489261319192039085612d8c565b936001600160a01b031690565b505050565b906131ad90613090565b6131c2611d77609e546001600160801b031690565b806131e2575b50806131d2575050565b612c7a610b6c916110e993612709565b9061324b7f3623a54e8078be0d90ecfbef82da6a31ff3e6be8aa1718e7a7f3d0d33ff1d32a916116e461322f612c7a61322561321e89896123f1565b8886612d8c565b8080950397612709565b6001600160801b03166001600160801b0319609e541617609e55565b0390a15f6131c8565b906001600160801b03809116911601906001600160801b0382116107c557565b61328e3031613288611d7760995460801c90565b90612709565b90811561347657609e546132aa6001600160801b038216611d77565b90816133e2575b50506132c8611d776099546001600160801b031690565b91821580156133da575b6133d4576132ef906132e3846123ad565b90818082109118021890565b80156133d4576132fe81614041565b9283156133cd57610b616110e99261333b61331f612c7a88610b6c96612709565b6001600160801b03166001600160801b03196099541617609955565b613355611bb261334a83613fc4565b60995460801c613254565b61335f8187614344565b60408051878152602081018390527f624ea167e477f9d39f7f4094b9dfe2e6346eb4a7aada54338db51abd554c4b9f9190a1612c7a6133b16133a088613fc4565b6098546001600160801b031661235b565b6001600160801b03166001600160801b03196098541617609855565b505f925050565b505f9150565b5080156132d2565b81849294106133cd5761334a84613446937f624ea167e477f9d39f7f4094b9dfe2e6346eb4a7aada54338db51abd554c4b9f82613425611d77611bb29760801c90565b61342f8282614344565b604080519182526020820192909252a10394613fc4565b61345a6001600160801b03609e5416609e55565b61346f6001600160801b0319609e5416609e55565b5f806132b1565b5f9150565b9392919091613488612e17565b613490613e2f565b6001600160a01b0385165f908152610137602052604090206134b190612322565b946001600160801b036134cb87516001600160801b031690565b1615613696576134da86612bcf565b6001600160a01b0381165f908152609c60205260409020613503906134fe906109d1565b614451565b955f198414613666575b6040516329460cc560e11b81526001600160a01b03861660048201526024810185905292602084806044810103815f7f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165af19384156106ec575f94613645575b5083976135a561359861358888613fc4565b85516001600160801b0316613254565b6001600160801b03168452565b6135b9611d7784516001600160801b031690565b11613636577fa16d97739893e1436c9753925fb5cef174c4f368699dc86cc8fdb0e6e60f8e5894613602613631936106578660018060a01b03165f5261013760205260405f2090565b604080516001600160a01b03988916815260208101969096528501528516606084015293169281906080820190565b0390a2565b633684c65960e01b5f5260045ffd5b61365f91945060203d6020116106e5576106d781836110b9565b925f613576565b925061367c611d7784516001600160801b031690565b8087111561368c5786039261350d565b505f955050505050565b60405163752a536d60e01b81526020816004817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165afa9081156106ec57613701916136f1915f91613706575b50613fc4565b6001600160801b03166020880152565b6134da565b61371f915060203d6020116106e5576106d781836110b9565b5f6136eb565b92906001600160a01b03811615610c4d5761373e613e2f565b7f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03811691823b1561037357604051631d8557d760e01b81525f8160048183885af180156106ec57613977575b506001600160a01b0386165f908152610137602052604090206137b490612322565b946001600160801b036137ce87516001600160801b031690565b16156106ae576137dd86612bcf565b6137f3602061097288516001600160801b031690565b0381885afa80156106ec5761385c946020925f92613958575b506001600160a01b038a165f908152609c6020526040902061382d906109d1565b61383561246a565b9061383e6110da565b938452848401526040830185905260608301525f6080830152610a03565b038173b3ce222b28fda660c1375246a179d2acce4a0f745af49283156106ec575f93613937575b50604051633b9e9f0160e21b81523360048201526024810182905292959293869390602090869060449082905f905af19182156106ec57610636610b186138d6926110e9986138f39661391a5750613fc4565b6001600160a01b0388165f9081526101376020526040902061237b565b6139156138ff83614041565b8097613910610b6c610b6187613fc4565b614577565b613dd4565b6139329060203d6020116106e5576106d781836110b9565b6136eb565b61395191935060203d6020116106e5576106d781836110b9565b915f613883565b613970919250833d85116106e5576106d781836110b9565b905f61380c565b80610c415f613985936110b9565b5f613792565b609a548061399857505f90565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be301546001600160a01b0316610356565b805490816139dc57505f919050565b5f90815260209020015f1901546001600160a01b0316610356565b919091613a026146ec565b6040820151613a0f6146ec565b613a1882613d0c565b7f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf613a4e6040519260208452602084019061200e565b918033930390a2613a6a61ffff602084015116916122776146ec565b613a72613e2f565b61271061ffff82161161051b576065805467ffffffffffffffff60b01b4260b01b1669ffffffffffffffffffff60a01b1990911661ffff60a01b60a085901b161717905560405161ffff909116815233907f96e06190cc0894376ae1c3ba56586573ed2786a831b68ad88c6a0410d331630490602090a25190613af36146ec565b8115613b2557613b149160018101613b1c575b50613b0f614717565b614727565b6110e9614757565b609d555f613b06565b6331278a8760e01b5f5260045ffd5b6001600160a01b037f000000000000000000000000927a83c679a5e1a6435d6bfaef7f20d4db23e2cc16308114908115613b70575b5061141a57565b5f80516020614bf2833981519152546001600160a01b0316141590505f613b69565b60025f80516020614c128339815191525414613bbb5760025f80516020614c1283398151915255565b633ee5aeb560e01b5f5260045ffd5b95939192613bfd936108359896928852602088015260018060a01b0316604087015260a0606087015260a086019161263a565b92608081850391015261263a565b60d2546001600160a01b0316939092918480158015613d04575b613cf9578615613ced575094613c8291602095967f000000000000000000000000000000000000000000000000000000000000000146145f14613cdf5760d354955b604051630b1192e160e11b8152988997889760048901613bca565b0381732319ace2793561520fb600da1c4b09a660a4adb55af49081156106ec575f91613cc0575b5080613cb25790565b610835600160d5540160d555565b613cd9915060203d6020116112b9576112ab81836110b9565b5f613ca9565b613ce76148cd565b95613c67565b95505050505050331490565b505050505050505f90565b508115613c25565b6001600160a01b03168015610c4d575f546001600160a01b03811682146118555781906001600160601b0360a01b16175f55337f101b8081ff3b56bbf45deb824d86a3b0fd38b7e3dd42421105cf8abe9106db0b5f80a3565b604051630156a69560e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa9081156106ec575f91613dbb575090565b610835915060203d6020116112b9576112ab81836110b9565b613ddc613b92565b814710613e18575f918291829182916001600160a01b03165af1613dfe613ff5565b901561194f575060015f80516020614c1283398151915255565b504763cf47918160e01b5f5260045260245260445ffd5b604051633eb1acf760e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa9081156106ec575f91613e98575b50613e8957565b63e775715160e01b5f5260045ffd5b613eb1915060203d6020116112b9576112ab81836110b9565b5f613e82565b949392613ee3606093613ed5612a9d9460808a5260808a019061200e565b9088820360208a015261200e565b90868203604088015261200e565b8051613efb61246a565b5f927f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa6001600160a01b0316915b838510613f37575050505050565b613f418583612ad2565b519060808201908151928051906040810151946060602083015192015195883b15610373575f93613f88604051988995869485946304512a2360e31b865260048601613eb7565b0391895af19182156106ec57600193613fa893613fb0575b505190612709565b940193613f29565b80610c415f613fbe936110b9565b5f613fa0565b6001600160801b038111613fde576001600160801b031690565b6306dfcc6560e41b5f52608060045260245260445ffd5b3d1561401f573d90614006826110fa565b9161401460405193846110b9565b82523d5f602084013e565b606090565b5f8061083593602081519101845af461403b613ff5565b91614b54565b609854906001600160801b03821681158015614070575b156140635750905090565b6108359260801c91612d8c565b508015614058565b6098546001600160801b03811690821580156140be575b1561409957505090565b60801c906140a8828285612d8c565b928215612bca5709151581018091116107c55790565b50811561408f565b6140ce613e2f565b6001600160a01b03168015614126576065546001600160a01b0381168214611855576001600160a01b0319168117606555337faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d35f80a3565b630ed1b8b360e31b5f5260045ffd5b919061413f613e2f565b6001600160a01b038316908115610c4d5780156107ca5760985460801c8101936141676126c4565b85116141cb57610b6c9461418e9161418961418185614078565b978893613fc4565b6142a5565b60408051918252602082018590525f9082015233907f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c90606090a3565b6304ffa0ff60e51b5f5260045ffd5b909291926141e6613e2f565b6001600160a01b038216918215610c4d5781156107ca5760985460801c820161420d6126c4565b81116141cb57610b6c9561425461427d927f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c9461418961424c88614078565b9a8b93613fc4565b60408051948552602085018890526001600160a01b039091169084015233929081906060820190565b0390a3565b905f198183099102908180821091030391565b634e487b715f526020526024601cfd5b6142ae82613fc4565b6001600160801b036142c560985492828416613254565b16906001600160801b0319161760985560018060a01b03165f52609c60205260405f20908154019055565b8054680100000000000000008110156110995760018101808355811015612acd575f9182526020918290208351939092015160a01b6001600160a01b0319166001600160a01b039390931692909217910155565b91909180156143ee57614357609a6139cd565b9081018091116107c5576001600160a01b0381116143d7576001600160601b0383116143bf576143b86001600160601b036110e993946143a76143986110eb565b6001600160a01b039095168552565b166001600160601b03166020830152565b609a6142f0565b826306dfcc6560e41b5f52606060045260245260445ffd5b6306dfcc6560e41b5f5260a060045260245260445ffd5b632ec8835b60e21b5f5260045ffd5b51906001600160401b038216820361037357565b908160609103126103735761256f604080519261442d8461109e565b80516144388161053f565b8452614446602082016143fd565b6020850152016143fd565b60405163e48a5f7b60e01b81523060048201526060816024816001600160a01b037f000000000000000000000000287d1e2a8de183a8bf8f2b09fa1340fbd766eb59165afa80156106ec576144f0926001600160401b036144ca60406144d1946020965f91614548575b5001516001600160401b031690565b1690612d1a565b604051809381926363737ac960e11b8352600483019190602083019252565b03817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165afa9081156106ec575f9161452f575090565b610835915060203d6020116106e5576106d781836110b9565b61456a915060603d606011614570575b61456281836110b9565b810190614411565b5f6144bb565b503d614558565b60018060a01b03165f52609c60205260405f209081548181039081116107c5576145a19255613fc4565b6001600160801b03609854918183160316906001600160801b03191617609855565b6145cb6146ec565b6145d3614b99565b60d0548061462d575b5060d2546001600160a01b0316156145f057565b60d280546001600160a01b0319166001600160a01b037f00000000000000000000000075ab6ddce07556639333d3df1eaa684f5735223e16179055565b60d15460d2549091906001600160a01b037f00000000000000000000000075ab6ddce07556639333d3df1eaa684f5735223e8116911690803b15610373576040516376615a5560e01b8152600481019390935260248301939093526001600160a01b03166044820152905f908290606490829084905af180156106ec576146d8575b506146b95f60d155565b6146c25f60d055565b60d280546001600160a01b03191690555f6145dc565b80610c415f6146e6936110b9565b5f6146af565b60ff5f80516020614c328339815191525460401c161561470857565b631afcd79f60e31b5f5260045ffd5b61471f6146ec565b6110e9614b99565b61472f6146ec565b6001600160a01b0316806147405750565b6001600160601b0360a01b61016a54161761016a55565b61475f6146ec565b633b9aca003410614774576124423430614135565b63ea2559bb60e01b5f5260045ffd5b90816020910312610373575160ff811681036103735790565b6040516352d1902d60e01b81529091906020816004816001600160a01b0387165afa5f91816148ac575b506147e757634c9c8ce360e01b5f526001600160a01b03831660045260245ffd5b5f80516020614bf2833981519152810361489a5750813b15614879575f80516020614bf283398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156148615761244291614024565b50503461486a57565b63b398979f60e01b5f5260045ffd5b50634c9c8ce360e01b5f9081526001600160a01b0391909116600452602490fd5b632a87526960e21b5f5260045260245ffd5b6148c691925060203d6020116106e5576106d781836110b9565b905f6147c6565b60405163703f96f760e11b815260406004820152600f60448201526e5661756c7456616c696461746f727360881b60648201523060248201526020816084817326314bd892cea77e2731ebbfb7c47af0bdcfe7735af49081156106ec575f9161452f575090565b60018060a01b0381165f5261013760205260405f20906001600160801b0380604051936149608561107e565b548181169081865260801c6020860152161615612fb557611d776149b16134fe6109d16149bf9461498f613e2f565b61499887612bcf565b6001600160a01b03165f908152609c6020526040902090565b92516001600160801b031690565b1161363657565b909291928015611ece576001600160a01b038416918215610c4d576149ec6114f7613d65565b614ae25761427d7f211091c5bf013c1230f996c3bb2bc327e3de429a3d3c356dcea9a0c858bc407f91614abf61331f85611bf99981614a94614a39611d776099546001600160801b031690565b926111c5614a6685614a61614a4c61398b565b614a5b611d77609e5460801c90565b906123f1565b6123f1565b9e8f6040519283916020830195429087604091949392606082019560018060a01b0316825260208201520152565b556001600160a01b0385165f908152609c60205260409020614ab7838254612709565b905501613fc4565b6040805188815260208101959095526001600160a01b0390911693918291820190565b91929390614aef816123ad565b9283156107ca57614b35847f5cdf07ad0fc222442720b108e3ed4c4640f0fadc2ab2253e66f259a0fea8348094614b2b610b6c610b6184613fc4565b6139158585614577565b6040805194855260208501929092526001600160a01b031692a35f1990565b90614b5f5750614bd3565b81511580614b90575b614b70575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b15614b68565b614ba16146ec565b614ba96146ec565b60015f80516020614c1283398151915255614bc26148cd565b60d3548103614bce5750565b60d355565b805115614be257805190602001fd5b63d6bda27560e01b5f5260045ffdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a164736f6c634300081a000a

Verified Source Code Partial Match

Compiler: v0.8.26+commit.8a97fa7a EVM: cancun Optimization: Yes (200 runs)
EthVault.sol 156 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IEthVault} from "../../interfaces/IEthVault.sol";
import {IEthVaultFactory} from "../../interfaces/IEthVaultFactory.sol";
import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol";
import {Multicall} from "../../base/Multicall.sol";
import {VaultValidators} from "../modules/VaultValidators.sol";
import {VaultAdmin} from "../modules/VaultAdmin.sol";
import {VaultFee} from "../modules/VaultFee.sol";
import {VaultVersion, IVaultVersion} from "../modules/VaultVersion.sol";
import {VaultImmutables} from "../modules/VaultImmutables.sol";
import {VaultState} from "../modules/VaultState.sol";
import {VaultEnterExit, IVaultEnterExit} from "../modules/VaultEnterExit.sol";
import {VaultOsToken} from "../modules/VaultOsToken.sol";
import {VaultEthStaking} from "../modules/VaultEthStaking.sol";
import {VaultMev} from "../modules/VaultMev.sol";

/**
 * @title EthVault
 * @author StakeWise
 * @notice Defines the Ethereum staking Vault
 */
contract EthVault is
    VaultImmutables,
    Initializable,
    VaultAdmin,
    VaultVersion,
    VaultFee,
    VaultState,
    VaultValidators,
    VaultEnterExit,
    VaultOsToken,
    VaultMev,
    VaultEthStaking,
    Multicall,
    IEthVault
{
    uint8 private constant _version = 5;

    /**
     * @dev Constructor
     * @dev Since the immutable variable value is stored in the bytecode,
     *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
     * @param args The arguments for initializing the EthVault contract
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(EthVaultConstructorArgs memory args)
        VaultImmutables(args.keeper, args.vaultsRegistry)
        VaultValidators(
            args.depositDataRegistry,
            args.validatorsRegistry,
            args.validatorsWithdrawals,
            args.validatorsConsolidations,
            args.consolidationsChecker
        )
        VaultEnterExit(args.exitingAssetsClaimDelay)
        VaultOsToken(args.osTokenVaultController, args.osTokenConfig, args.osTokenVaultEscrow)
        VaultMev(args.sharedMevEscrow)
    {
        _disableInitializers();
    }

    /// @inheritdoc IEthVault
    function initialize(bytes calldata params) external payable virtual override reinitializer(_version) {
        // if admin is already set, it's an upgrade from version 4 to 5
        if (admin != address(0)) {
            __EthVault_upgrade();
            return;
        }

        // initialize deployed vault
        __EthVault_init(
            IEthVaultFactory(msg.sender).vaultAdmin(),
            IEthVaultFactory(msg.sender).ownMevEscrow(),
            abi.decode(params, (EthVaultInitParams))
        );
    }

    /// @inheritdoc IEthVault
    function depositAndMintOsToken(address receiver, uint256 osTokenShares, address referrer)
        public
        payable
        override
        returns (uint256)
    {
        deposit(msg.sender, referrer);
        return mintOsToken(receiver, osTokenShares, referrer);
    }

    /// @inheritdoc IEthVault
    function updateStateAndDepositAndMintOsToken(
        address receiver,
        uint256 osTokenShares,
        address referrer,
        IKeeperRewards.HarvestParams calldata harvestParams
    ) external payable override returns (uint256) {
        updateState(harvestParams);
        return depositAndMintOsToken(receiver, osTokenShares, referrer);
    }

    /// @inheritdoc IVaultEnterExit
    function enterExitQueue(uint256 shares, address receiver)
        public
        virtual
        override(IVaultEnterExit, VaultEnterExit, VaultOsToken)
        returns (uint256 positionTicket)
    {
        return super.enterExitQueue(shares, receiver);
    }

    /// @inheritdoc VaultVersion
    function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) {
        return keccak256("EthVault");
    }

    /// @inheritdoc IVaultVersion
    function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) {
        return _version;
    }

    /**
     * @dev Upgrades the EthVault contract
     */
    function __EthVault_upgrade() internal {
        __VaultValidators_upgrade();
    }

    /**
     * @dev Initializes the EthVault contract
     * @param admin The address of the admin of the Vault
     * @param ownMevEscrow The address of the MEV escrow owned by the Vault. Zero address if shared MEV escrow is used.
     * @param params The decoded parameters for initializing the EthVault contract
     */
    function __EthVault_init(address admin, address ownMevEscrow, EthVaultInitParams memory params)
        internal
        onlyInitializing
    {
        __VaultAdmin_init(admin, params.metadataIpfsHash);
        // fee recipient is initially set to admin address
        __VaultFee_init(admin, params.feePercent);
        __VaultState_init(params.capacity);
        __VaultValidators_init();
        __VaultMev_init(ownMevEscrow);
        __VaultEthStaking_init();
    }

    /**
     * @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[50] private __gap;
}
Initializable.sol 238 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @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 Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 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 in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reinitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._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 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._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() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @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 {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

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

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        bytes32 slot = _initializableStorageSlot();
        assembly {
            $.slot := slot
        }
    }
}
IEthVault.sol 110 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperRewards} from "./IKeeperRewards.sol";
import {IVaultAdmin} from "./IVaultAdmin.sol";
import {IVaultVersion} from "./IVaultVersion.sol";
import {IVaultFee} from "./IVaultFee.sol";
import {IVaultState} from "./IVaultState.sol";
import {IVaultValidators} from "./IVaultValidators.sol";
import {IVaultEnterExit} from "./IVaultEnterExit.sol";
import {IVaultOsToken} from "./IVaultOsToken.sol";
import {IVaultMev} from "./IVaultMev.sol";
import {IVaultEthStaking} from "./IVaultEthStaking.sol";
import {IMulticall} from "./IMulticall.sol";

/**
 * @title IEthVault
 * @author StakeWise
 * @notice Defines the interface for the EthVault contract
 */
interface IEthVault is
    IVaultAdmin,
    IVaultVersion,
    IVaultFee,
    IVaultState,
    IVaultValidators,
    IVaultEnterExit,
    IVaultOsToken,
    IVaultMev,
    IVaultEthStaking,
    IMulticall
{
    /**
     * @dev Struct for deploying the EthVault contract
     * @param keeper The address of the Keeper contract
     * @param vaultsRegistry The address of the VaultsRegistry contract
     * @param validatorsRegistry The contract address used for registering validators in beacon chain
     * @param validatorsWithdrawals The contract address used for withdrawing validators in beacon chain
     * @param validatorsConsolidations The contract address used for consolidating validators in beacon chain
     * @param consolidationsChecker The contract address used for checking consolidations
     * @param osTokenVaultController The address of the OsTokenVaultController contract
     * @param osTokenConfig The address of the OsTokenConfig contract
     * @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract
     * @param sharedMevEscrow The address of the shared MEV escrow
     * @param depositDataRegistry The address of the DepositDataRegistry contract
     * @param exitingAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking
     */
    struct EthVaultConstructorArgs {
        address keeper;
        address vaultsRegistry;
        address validatorsRegistry;
        address validatorsWithdrawals;
        address validatorsConsolidations;
        address consolidationsChecker;
        address osTokenVaultController;
        address osTokenConfig;
        address osTokenVaultEscrow;
        address sharedMevEscrow;
        address depositDataRegistry;
        uint64 exitingAssetsClaimDelay;
    }

    /**
     * @dev Struct for initializing the EthVault contract
     * @param capacity The Vault stops accepting deposits after exceeding the capacity
     * @param feePercent The fee percent that is charged by the Vault
     * @param metadataIpfsHash The IPFS hash of the Vault's metadata file
     */
    struct EthVaultInitParams {
        uint256 capacity;
        uint16 feePercent;
        string metadataIpfsHash;
    }

    /**
     * @notice Initializes or upgrades the EthVault contract. Must transfer security deposit during the deployment.
     * @param params The encoded parameters for initializing the EthVault contract
     */
    function initialize(bytes calldata params) external payable;

    /**
     * @notice Deposits assets to the vault and mints OsToken shares to the receiver
     * @param receiver The address to receive the OsToken
     * @param osTokenShares The amount of OsToken shares to mint.
     *        If set to type(uint256).max, max OsToken shares will be minted.
     * @param referrer The address of the referrer
     * @return The amount of OsToken assets minted
     */
    function depositAndMintOsToken(address receiver, uint256 osTokenShares, address referrer)
        external
        payable
        returns (uint256);

    /**
     * @notice Updates the state, deposits assets to the vault and mints OsToken shares to the receiver
     * @param receiver The address to receive the OsToken
     * @param osTokenShares The amount of OsToken shares to mint.
     *        If set to type(uint256).max, max OsToken shares will be minted.
     * @param referrer The address of the referrer
     * @param harvestParams The parameters for the harvest
     * @return The amount of OsToken assets minted
     */
    function updateStateAndDepositAndMintOsToken(
        address receiver,
        uint256 osTokenShares,
        address referrer,
        IKeeperRewards.HarvestParams calldata harvestParams
    ) external payable returns (uint256);
}
IEthVaultFactory.sol 45 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title IEthVaultFactory
 * @author StakeWise
 * @notice Defines the interface for the ETH Vault Factory contract
 */
interface IEthVaultFactory {
    /**
     * @notice Event emitted on a Vault creation
     * @param admin The address of the Vault admin
     * @param vault The address of the created Vault
     * @param ownMevEscrow The address of the own MEV escrow contract. Zero address if shared MEV escrow is used.
     * @param params The encoded parameters for initializing the Vault contract
     */
    event VaultCreated(address indexed admin, address indexed vault, address ownMevEscrow, bytes params);

    /**
     * @notice The address of the Vault implementation contract used for proxy creation
     * @return The address of the Vault implementation contract
     */
    function implementation() external view returns (address);

    /**
     * @notice The address of the own MEV escrow contract used for Vault creation
     * @return The address of the MEV escrow contract
     */
    function ownMevEscrow() external view returns (address);

    /**
     * @notice The address of the Vault admin used for Vault creation
     * @return The address of the Vault admin
     */
    function vaultAdmin() external view returns (address);

    /**
     * @notice Create Vault. Must transfer security deposit together with a call.
     * @param params The encoded parameters for initializing the Vault contract
     * @param isOwnMevEscrow Whether to deploy own escrow contract or connect to a smoothing pool for priority fees and MEV rewards
     * @return vault The address of the created Vault
     */
    function createVault(bytes calldata params, bool isOwnMevEscrow) external payable returns (address vault);
}
IKeeperRewards.sol 199 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperOracles} from "./IKeeperOracles.sol";

/**
 * @title IKeeperRewards
 * @author StakeWise
 * @notice Defines the interface for the Keeper contract rewards
 */
interface IKeeperRewards is IKeeperOracles {
    /**
     * @notice Event emitted on rewards update
     * @param caller The address of the function caller
     * @param rewardsRoot The new rewards merkle tree root
     * @param avgRewardPerSecond The new average reward per second
     * @param updateTimestamp The update timestamp used for rewards calculation
     * @param nonce The nonce used for verifying signatures
     * @param rewardsIpfsHash The new rewards IPFS hash
     */
    event RewardsUpdated(
        address indexed caller,
        bytes32 indexed rewardsRoot,
        uint256 avgRewardPerSecond,
        uint64 updateTimestamp,
        uint64 nonce,
        string rewardsIpfsHash
    );

    /**
     * @notice Event emitted on Vault harvest
     * @param vault The address of the Vault
     * @param rewardsRoot The rewards merkle tree root
     * @param totalAssetsDelta The Vault total assets delta since last sync. Can be negative in case of penalty/slashing.
     * @param unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
     */
    event Harvested(
        address indexed vault, bytes32 indexed rewardsRoot, int256 totalAssetsDelta, uint256 unlockedMevDelta
    );

    /**
     * @notice Event emitted on rewards min oracles number update
     * @param oracles The new minimum number of oracles required to update rewards
     */
    event RewardsMinOraclesUpdated(uint256 oracles);

    /**
     * @notice A struct containing the last synced Vault's cumulative reward
     * @param assets The Vault cumulative reward earned since the start. Can be negative in case of penalty/slashing.
     * @param nonce The nonce of the last sync
     */
    struct Reward {
        int192 assets;
        uint64 nonce;
    }

    /**
     * @notice A struct containing the last unlocked Vault's cumulative execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
     * @param assets The shared MEV Vault's cumulative execution reward that can be withdrawn
     * @param nonce The nonce of the last sync
     */
    struct UnlockedMevReward {
        uint192 assets;
        uint64 nonce;
    }

    /**
     * @notice A struct containing parameters for rewards update
     * @param rewardsRoot The new rewards merkle root
     * @param avgRewardPerSecond The new average reward per second
     * @param updateTimestamp The update timestamp used for rewards calculation
     * @param rewardsIpfsHash The new IPFS hash with all the Vaults' rewards for the new root
     * @param signatures The concatenation of the Oracles' signatures
     */
    struct RewardsUpdateParams {
        bytes32 rewardsRoot;
        uint256 avgRewardPerSecond;
        uint64 updateTimestamp;
        string rewardsIpfsHash;
        bytes signatures;
    }

    /**
     * @notice A struct containing parameters for harvesting rewards. Can only be called by Vault.
     * @param rewardsRoot The rewards merkle root
     * @param reward The Vault cumulative reward earned since the start. Can be negative in case of penalty/slashing.
     * @param unlockedMevReward The Vault cumulative execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
     * @param proof The proof to verify that Vault's reward is correct
     */
    struct HarvestParams {
        bytes32 rewardsRoot;
        int160 reward;
        uint160 unlockedMevReward;
        bytes32[] proof;
    }

    /**
     * @notice Previous Rewards Root
     * @return The previous merkle tree root of the rewards accumulated by the Vaults
     */
    function prevRewardsRoot() external view returns (bytes32);

    /**
     * @notice Rewards Root
     * @return The latest merkle tree root of the rewards accumulated by the Vaults
     */
    function rewardsRoot() external view returns (bytes32);

    /**
     * @notice Rewards Nonce
     * @return The nonce used for updating rewards merkle tree root
     */
    function rewardsNonce() external view returns (uint64);

    /**
     * @notice The last rewards update
     * @return The timestamp of the last rewards update
     */
    function lastRewardsTimestamp() external view returns (uint64);

    /**
     * @notice The minimum number of oracles required to update rewards
     * @return The minimum number of oracles
     */
    function rewardsMinOracles() external view returns (uint256);

    /**
     * @notice The rewards delay
     * @return The delay in seconds between rewards updates
     */
    function rewardsDelay() external view returns (uint256);

    /**
     * @notice Get last synced Vault cumulative reward
     * @param vault The address of the Vault
     * @return assets The last synced reward assets
     * @return nonce The last synced reward nonce
     */
    function rewards(address vault) external view returns (int192 assets, uint64 nonce);

    /**
     * @notice Get last unlocked shared MEV Vault cumulative reward
     * @param vault The address of the Vault
     * @return assets The last synced reward assets
     * @return nonce The last synced reward nonce
     */
    function unlockedMevRewards(address vault) external view returns (uint192 assets, uint64 nonce);

    /**
     * @notice Checks whether Vault must be harvested
     * @param vault The address of the Vault
     * @return `true` if the Vault requires harvesting, `false` otherwise
     */
    function isHarvestRequired(address vault) external view returns (bool);

    /**
     * @notice Checks whether the Vault can be harvested
     * @param vault The address of the Vault
     * @return `true` if Vault can be harvested, `false` otherwise
     */
    function canHarvest(address vault) external view returns (bool);

    /**
     * @notice Checks whether rewards can be updated
     * @return `true` if rewards can be updated, `false` otherwise
     */
    function canUpdateRewards() external view returns (bool);

    /**
     * @notice Checks whether the Vault has registered validators
     * @param vault The address of the Vault
     * @return `true` if Vault is collateralized, `false` otherwise
     */
    function isCollateralized(address vault) external view returns (bool);

    /**
     * @notice Update rewards data
     * @param params The struct containing rewards update parameters
     */
    function updateRewards(RewardsUpdateParams calldata params) external;

    /**
     * @notice Harvest rewards. Can be called only by Vault.
     * @param params The struct containing rewards harvesting parameters
     * @return totalAssetsDelta The total reward/penalty accumulated by the Vault since the last sync
     * @return unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
     * @return harvested `true` when the rewards were harvested, `false` otherwise
     */
    function harvest(HarvestParams calldata params)
        external
        returns (int256 totalAssetsDelta, uint256 unlockedMevDelta, bool harvested);

    /**
     * @notice Set min number of oracles for confirming rewards update. Can only be called by the owner.
     * @param _rewardsMinOracles The new min number of oracles for confirming rewards update
     */
    function setRewardsMinOracles(uint256 _rewardsMinOracles) external;
}
Multicall.sol 23 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import "../interfaces/IMulticall.sol";

/**
 * @title Multicall
 * @author StakeWise
 * @notice Enables calling multiple methods in a single call to the contract
 */
abstract contract Multicall is IMulticall {
    /// @inheritdoc IMulticall
    function multicall(bytes[] calldata data) external override returns (bytes[] memory results) {
        uint256 dataLength = data.length;
        results = new bytes[](dataLength);
        for (uint256 i = 0; i < dataLength; i++) {
            bytes memory result = Address.functionDelegateCall(address(this), data[i]);
            results[i] = result;
        }
    }
}
VaultValidators.sol 289 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {IKeeperValidators} from "../../interfaces/IKeeperValidators.sol";
import {IVaultValidators} from "../../interfaces/IVaultValidators.sol";
import {IConsolidationsChecker} from "../../interfaces/IConsolidationsChecker.sol";
import {IDepositDataRegistry} from "../../interfaces/IDepositDataRegistry.sol";
import {Errors} from "../../libraries/Errors.sol";
import {ValidatorUtils} from "../../libraries/ValidatorUtils.sol";
import {EIP712Utils} from "../../libraries/EIP712Utils.sol";
import {VaultImmutables} from "./VaultImmutables.sol";
import {VaultAdmin} from "./VaultAdmin.sol";
import {VaultState} from "./VaultState.sol";

/**
 * @title VaultValidators
 * @author StakeWise
 * @notice Defines the validators functionality for the Vault
 */
abstract contract VaultValidators is
    VaultImmutables,
    Initializable,
    ReentrancyGuardUpgradeable,
    VaultAdmin,
    VaultState,
    IVaultValidators
{
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _depositDataRegistry;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    uint256 private immutable _initialChainId;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address internal immutable _validatorsRegistry;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _validatorsWithdrawals;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _validatorsConsolidations;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _consolidationsChecker;

    /// deprecated. Deposit data management is moved to DepositDataRegistry contract
    bytes32 private __deprecated__validatorsRoot;

    /// deprecated. Deposit data management is moved to DepositDataRegistry contract
    uint256 private __deprecated__validatorIndex;

    /// @inheritdoc IVaultValidators
    address public override validatorsManager;

    bytes32 private _initialDomainSeparator;

    /// @inheritdoc IVaultValidators
    mapping(bytes32 publicKeyHash => bool isRegistered) public override v2Validators;

    /// @inheritdoc IVaultValidators
    uint256 public override validatorsManagerNonce;

    /**
     * @dev Constructor
     * @dev Since the immutable variable value is stored in the bytecode,
     *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
     * @param depositDataRegistry The address of the deposit data registry contract
     * @param validatorsRegistry The contract address used for registering validators in beacon chain
     * @param validatorsWithdrawals The contract address used for withdrawing validators in beacon chain
     * @param validatorsConsolidations The contract address used for consolidating validators in beacon chain
     * @param consolidationsChecker The contract address used for verifying consolidation approvals
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(
        address depositDataRegistry,
        address validatorsRegistry,
        address validatorsWithdrawals,
        address validatorsConsolidations,
        address consolidationsChecker
    ) {
        _initialChainId = block.chainid;
        _depositDataRegistry = depositDataRegistry;
        _validatorsRegistry = validatorsRegistry;
        _validatorsWithdrawals = validatorsWithdrawals;
        _validatorsConsolidations = validatorsConsolidations;
        _consolidationsChecker = consolidationsChecker;
    }

    /// @inheritdoc IVaultValidators
    function registerValidators(
        IKeeperValidators.ApprovalParams calldata keeperParams,
        bytes calldata validatorsManagerSignature
    ) external override {
        // check whether oracles have approve validators registration
        IKeeperValidators(_keeper).approveValidators(keeperParams);

        // check vault is up to date
        _checkHarvested();

        // check access
        if (
            !_isValidatorsManager(
                keeperParams.validators, keeperParams.validatorsRegistryRoot, validatorsManagerSignature
            )
        ) {
            revert Errors.AccessDenied();
        }

        // get validator deposits
        ValidatorUtils.ValidatorDeposit[] memory validatorDeposits =
            ValidatorUtils.getValidatorDeposits(v2Validators, keeperParams.validators, false);

        // register validators
        _registerValidators(validatorDeposits);
    }

    /// @inheritdoc IVaultValidators
    function fundValidators(bytes calldata validators, bytes calldata validatorsManagerSignature) external override {
        // check vault is up to date
        _checkHarvested();

        // check access
        if (!_isValidatorsManager(validators, bytes32(validatorsManagerNonce), validatorsManagerSignature)) {
            revert Errors.AccessDenied();
        }

        // get validator deposits
        ValidatorUtils.ValidatorDeposit[] memory validatorDeposits =
            ValidatorUtils.getValidatorDeposits(v2Validators, validators, true);

        // top up validators
        _registerValidators(validatorDeposits);
    }

    /// @inheritdoc IVaultValidators
    function withdrawValidators(bytes calldata validators, bytes calldata validatorsManagerSignature)
        external
        payable
        override
        nonReentrant
    {
        _checkCollateralized();
        if (!_isValidatorsManager(validators, bytes32(validatorsManagerNonce), validatorsManagerSignature)) {
            revert Errors.AccessDenied();
        }
        ValidatorUtils.withdrawValidators(validators, _validatorsWithdrawals);
    }

    /// @inheritdoc IVaultValidators
    function consolidateValidators(
        bytes calldata validators,
        bytes calldata validatorsManagerSignature,
        bytes calldata oracleSignatures
    ) external payable override nonReentrant {
        _checkCollateralized();
        if (!_isValidatorsManager(validators, bytes32(validatorsManagerNonce), validatorsManagerSignature)) {
            revert Errors.AccessDenied();
        }

        // Check for oracle approval if signatures provided
        bool consolidationsApproved = false;
        if (oracleSignatures.length > 0) {
            // Check whether oracles have approved validators consolidation
            IConsolidationsChecker(_consolidationsChecker).verifySignatures(address(this), validators, oracleSignatures);
            consolidationsApproved = true;
        }

        ValidatorUtils.consolidateValidators(
            v2Validators, validators, consolidationsApproved, _validatorsConsolidations
        );
    }

    /// @inheritdoc IVaultValidators
    function setValidatorsManager(address _validatorsManager) external override {
        _checkAdmin();
        if (_validatorsManager == validatorsManager) {
            revert Errors.ValueNotChanged();
        }

        // update validatorsManager address
        validatorsManager = _validatorsManager;
        emit ValidatorsManagerUpdated(msg.sender, _validatorsManager);
    }

    /**
     * @dev Internal function for registering validators
     * @param deposits The validators registration data
     */
    function _registerValidators(ValidatorUtils.ValidatorDeposit[] memory deposits) internal virtual;

    /**
     * @dev Internal function for checking whether the caller is the validators manager.
     *      If the valid signature is provided, update the nonce.
     * @param validators The concatenated validators data
     * @param nonce The nonce of the signature
     * @param validatorsManagerSignature The optional signature from the validators manager
     * @return true if the caller is the validators manager
     */
    function _isValidatorsManager(bytes calldata validators, bytes32 nonce, bytes calldata validatorsManagerSignature)
        internal
        returns (bool)
    {
        // SLOAD to memory
        address _validatorsManager = validatorsManager;
        if (_validatorsManager == address(0) || validators.length == 0) {
            return false;
        }

        if (validatorsManagerSignature.length == 0) {
            // if no signature is provided, check if the caller is the validators manager
            return msg.sender == _validatorsManager;
        }

        // check signature
        bytes32 domainSeparator =
            block.chainid == _initialChainId ? _initialDomainSeparator : _computeVaultValidatorsDomain();
        bool isValidSignature = ValidatorUtils.isValidManagerSignature(
            nonce, domainSeparator, _validatorsManager, validators, validatorsManagerSignature
        );

        // update signature nonce
        if (isValidSignature) {
            unchecked {
                // cannot realistically overflow
                validatorsManagerNonce += 1;
            }
        }

        return isValidSignature;
    }

    /**
     * @notice Computes the hash of the EIP712 typed data
     * @dev This function is used to compute the hash of the EIP712 typed data
     * @return The hash of the EIP712 typed data
     */
    function _computeVaultValidatorsDomain() private view returns (bytes32) {
        return EIP712Utils.computeDomainSeparator("VaultValidators", address(this));
    }

    /**
     * @dev Upgrades the VaultValidators contract
     */
    function __VaultValidators_upgrade() internal onlyInitializing {
        __VaultValidators_init_common();

        // migrate deposit data variables to DepositDataRegistry contract
        if (__deprecated__validatorsRoot != bytes32(0)) {
            IDepositDataRegistry(_depositDataRegistry).migrate(
                __deprecated__validatorsRoot, __deprecated__validatorIndex, validatorsManager
            );
            delete __deprecated__validatorIndex;
            delete __deprecated__validatorsRoot;
            delete validatorsManager;
        }
        if (validatorsManager == address(0)) {
            validatorsManager = _depositDataRegistry;
        }
    }

    /**
     * @dev Initializes the VaultValidators contract
     */
    function __VaultValidators_init() internal onlyInitializing {
        __VaultValidators_init_common();
    }

    /**
     * @dev Common initialization for gas optimization
     */
    function __VaultValidators_init_common() private {
        __ReentrancyGuard_init();
        // initialize domain separator
        bytes32 newInitialDomainSeparator = _computeVaultValidatorsDomain();
        if (newInitialDomainSeparator != _initialDomainSeparator) {
            _initialDomainSeparator = newInitialDomainSeparator;
        }
    }

    /**
     * @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[47] private __gap;
}
VaultAdmin.sol 63 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IVaultAdmin} from "../../interfaces/IVaultAdmin.sol";
import {Errors} from "../../libraries/Errors.sol";

/**
 * @title VaultAdmin
 * @author StakeWise
 * @notice Defines the admin functionality for the Vault
 */
abstract contract VaultAdmin is Initializable, IVaultAdmin {
    /// @inheritdoc IVaultAdmin
    address public override admin;

    /// @inheritdoc IVaultAdmin
    function setMetadata(string calldata metadataIpfsHash) external override {
        _checkAdmin();
        emit MetadataUpdated(msg.sender, metadataIpfsHash);
    }

    /// @inheritdoc IVaultAdmin
    function setAdmin(address newAdmin) external override {
        _checkAdmin();
        _setAdmin(newAdmin);
    }

    /**
     * @dev Internal method for checking whether the caller is admin
     */
    function _checkAdmin() internal view {
        if (msg.sender != admin) revert Errors.AccessDenied();
    }

    /**
     * @dev Internal method for updating the admin
     * @param newAdmin The address of the new admin
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) revert Errors.ZeroAddress();
        if (newAdmin == admin) revert Errors.ValueNotChanged();
        admin = newAdmin;
        emit AdminUpdated(msg.sender, newAdmin);
    }

    /**
     * @dev Initializes the VaultAdmin contract
     * @param _admin The address of the Vault admin
     */
    function __VaultAdmin_init(address _admin, string memory metadataIpfsHash) internal onlyInitializing {
        _setAdmin(_admin);
        emit MetadataUpdated(msg.sender, metadataIpfsHash);
    }

    /**
     * @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[50] private __gap;
}
VaultFee.sol 102 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IVaultFee} from "../../interfaces/IVaultFee.sol";
import {Errors} from "../../libraries/Errors.sol";
import {VaultAdmin} from "./VaultAdmin.sol";
import {VaultImmutables} from "./VaultImmutables.sol";

/**
 * @title VaultFee
 * @author StakeWise
 * @notice Defines the fee functionality for the Vault
 */
abstract contract VaultFee is VaultImmutables, Initializable, VaultAdmin, IVaultFee {
    uint256 internal constant _maxFeePercent = 10_000; // @dev 100.00 %
    uint256 private constant _feeUpdateDelay = 3 days;
    uint256 private constant _feeUpdateMultiplier = 120;
    uint256 private constant _feeUpdateBase = 100;

    /// @inheritdoc IVaultFee
    address public override feeRecipient;

    /// @inheritdoc IVaultFee
    uint16 public override feePercent;

    uint64 private _lastUpdateTimestamp;

    /// @inheritdoc IVaultFee
    function setFeeRecipient(address _feeRecipient) external override {
        _checkAdmin();
        _setFeeRecipient(_feeRecipient);
    }

    /// @inheritdoc IVaultFee
    function setFeePercent(uint16 _feePercent) external override {
        _checkAdmin();
        _setFeePercent(_feePercent, false);
    }

    /**
     * @dev Internal function for updating the fee recipient externally or from the initializer
     * @param _feeRecipient The address of the new fee recipient
     */
    function _setFeeRecipient(address _feeRecipient) private {
        _checkHarvested();
        if (_feeRecipient == address(0)) revert Errors.InvalidFeeRecipient();
        if (_feeRecipient == feeRecipient) revert Errors.ValueNotChanged();

        // update fee recipient address
        feeRecipient = _feeRecipient;
        emit FeeRecipientUpdated(msg.sender, _feeRecipient);
    }

    /**
     * @dev Internal function for updating the fee percent
     * @param _feePercent The new fee percent
     * @param isVaultCreation Flag indicating whether the fee percent is set during the vault creation
     */
    function _setFeePercent(uint16 _feePercent, bool isVaultCreation) private {
        _checkHarvested();
        if (_feePercent > _maxFeePercent) revert Errors.InvalidFeePercent();

        if (!isVaultCreation) {
            if (_lastUpdateTimestamp + _feeUpdateDelay > block.timestamp) {
                revert Errors.TooEarlyUpdate();
            }

            // check that the fee percent can be increase only by 20% at a time
            // if the current fee is 0, then it cannot exceed 1% initially
            uint256 currentFeePercent = feePercent;
            uint256 maxFeePercent =
                currentFeePercent > 0 ? (currentFeePercent * _feeUpdateMultiplier) / _feeUpdateBase : _feeUpdateBase;
            if (maxFeePercent < _feePercent) {
                revert Errors.InvalidFeePercent();
            }
        }

        // update fee percent
        feePercent = _feePercent;
        _lastUpdateTimestamp = uint64(block.timestamp);
        emit FeePercentUpdated(msg.sender, _feePercent);
    }

    /**
     * @dev Initializes the VaultFee contract
     * @param _feeRecipient The address of the fee recipient
     * @param _feePercent The fee percent that is charged by the Vault
     */
    function __VaultFee_init(address _feeRecipient, uint16 _feePercent) internal onlyInitializing {
        _setFeeRecipient(_feeRecipient);
        _setFeePercent(_feePercent, true);
    }

    /**
     * @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[50] private __gap;
}
VaultVersion.sol 57 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {IVaultsRegistry} from "../../interfaces/IVaultsRegistry.sol";
import {IVaultVersion} from "../../interfaces/IVaultVersion.sol";
import {Errors} from "../../libraries/Errors.sol";
import {VaultAdmin} from "./VaultAdmin.sol";
import {VaultImmutables} from "./VaultImmutables.sol";

/**
 * @title VaultVersion
 * @author StakeWise
 * @notice Defines the versioning functionality for the Vault
 */
abstract contract VaultVersion is VaultImmutables, Initializable, UUPSUpgradeable, VaultAdmin, IVaultVersion {
    bytes4 private constant _initSelector = bytes4(keccak256("initialize(bytes)"));

    /// @inheritdoc IVaultVersion
    function implementation() external view override returns (address) {
        return ERC1967Utils.getImplementation();
    }

    /// @inheritdoc UUPSUpgradeable
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable override onlyProxy {
        super.upgradeToAndCall(newImplementation, abi.encodeWithSelector(_initSelector, data));
    }

    /// @inheritdoc UUPSUpgradeable
    function _authorizeUpgrade(address newImplementation) internal view override {
        _checkAdmin();
        if (
            newImplementation == address(0) || ERC1967Utils.getImplementation() == newImplementation // cannot reinit the same implementation
                || IVaultVersion(newImplementation).vaultId() != vaultId() // vault must be of the same type
                || IVaultVersion(newImplementation).version() != version() + 1 // vault cannot skip versions between
                || !IVaultsRegistry(_vaultsRegistry).vaultImpls(newImplementation) // new implementation must be registered
        ) {
            revert Errors.UpgradeFailed();
        }
    }

    /// @inheritdoc IVaultVersion
    function vaultId() public pure virtual override returns (bytes32);

    /// @inheritdoc IVaultVersion
    function version() public pure virtual override returns (uint8);

    /**
     * @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[50] private __gap;
}
VaultImmutables.sol 54 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol";
import {Errors} from "../../libraries/Errors.sol";

/**
 * @title VaultImmutables
 * @author StakeWise
 * @notice Defines the Vault common immutable variables and check functions.
 */
abstract contract VaultImmutables {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address internal immutable _keeper;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address internal immutable _vaultsRegistry;

    /**
     * @dev Constructor
     * @dev Since the immutable variable value is stored in the bytecode,
     *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
     * @param keeper The address of the Keeper contract
     * @param vaultsRegistry The address of the VaultsRegistry contract
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address keeper, address vaultsRegistry) {
        _keeper = keeper;
        _vaultsRegistry = vaultsRegistry;
    }

    /**
     * @dev Internal method for checking whether the vault is harvested
     */
    function _checkHarvested() internal view virtual {
        if (IKeeperRewards(_keeper).isHarvestRequired(address(this))) revert Errors.NotHarvested();
    }

    /**
     * @dev Internal method for checking whether the vault is collateralized
     */
    function _checkCollateralized() internal view {
        if (!_isCollateralized()) revert Errors.NotCollateralized();
    }

    /**
     * @dev Returns whether the vault is collateralized
     * @return true if the vault is collateralized
     */
    function _isCollateralized() internal view virtual returns (bool) {
        return IKeeperRewards(_keeper).isCollateralized(address(this));
    }
}
VaultState.sol 378 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IVaultState} from "../../interfaces/IVaultState.sol";
import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol";
import {ExitQueue} from "../../libraries/ExitQueue.sol";
import {Errors} from "../../libraries/Errors.sol";
import {VaultImmutables} from "./VaultImmutables.sol";
import {VaultFee} from "./VaultFee.sol";

/**
 * @title VaultState
 * @author StakeWise
 * @notice Defines Vault's state manipulation
 */
abstract contract VaultState is VaultImmutables, Initializable, VaultFee, IVaultState {
    using ExitQueue for ExitQueue.History;

    uint128 internal _totalShares;
    uint128 internal _totalAssets;

    uint128 internal _queuedShares;
    uint128 internal _unclaimedAssets;
    ExitQueue.History internal _exitQueue;

    mapping(bytes32 => uint256) internal _exitRequests;
    mapping(address => uint256) internal _balances;

    uint256 private _capacity;

    uint128 internal _totalExitingAssets; // deprecated
    uint128 internal _totalExitingTickets; // deprecated
    uint256 internal _totalExitedTickets; // deprecated
    uint256 internal _donatedAssets;

    /// @inheritdoc IVaultState
    function totalShares() external view override returns (uint256) {
        return _totalShares;
    }

    /// @inheritdoc IVaultState
    function totalAssets() external view override returns (uint256) {
        return _totalAssets;
    }

    /// @inheritdoc IVaultState
    function getExitQueueData()
        external
        view
        override
        returns (
            uint128 queuedShares,
            uint128 unclaimedAssets,
            uint128 totalExitingTickets,
            uint128 totalExitingAssets,
            uint256 totalTickets
        )
    {
        return (
            _queuedShares,
            _unclaimedAssets,
            _totalExitingTickets,
            _totalExitingAssets,
            _exitQueue.getLatestTotalTickets()
        );
    }

    /// @inheritdoc IVaultState
    function getShares(address account) external view override returns (uint256) {
        return _balances[account];
    }

    /// @inheritdoc IVaultState
    function convertToShares(uint256 assets) public view override returns (uint256 shares) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /// @inheritdoc IVaultState
    function convertToAssets(uint256 shares) public view override returns (uint256 assets) {
        uint256 totalShares_ = _totalShares;
        return totalShares_ == 0 ? shares : Math.mulDiv(shares, _totalAssets, totalShares_);
    }

    /// @inheritdoc IVaultState
    function capacity() public view override returns (uint256) {
        // SLOAD to memory
        uint256 capacity_ = _capacity;

        // if capacity is not set, it is unlimited
        return capacity_ == 0 ? type(uint256).max : capacity_;
    }

    /// @inheritdoc IVaultState
    function withdrawableAssets() public view override returns (uint256) {
        uint256 vaultAssets = _vaultAssets();
        unchecked {
            // calculate assets that are reserved by users who queued for exit
            // cannot overflow as it is capped with underlying asset total supply
            uint256 reservedAssets = convertToAssets(_queuedShares) + _totalExitingAssets + _unclaimedAssets;
            return vaultAssets > reservedAssets ? vaultAssets - reservedAssets : 0;
        }
    }

    /// @inheritdoc IVaultState
    function isStateUpdateRequired() external view virtual override returns (bool) {
        return IKeeperRewards(_keeper).isHarvestRequired(address(this));
    }

    /// @inheritdoc IVaultState
    function updateState(IKeeperRewards.HarvestParams calldata harvestParams) public virtual override {
        // process total assets delta  since last update
        (int256 totalAssetsDelta, bool harvested) = _harvestAssets(harvestParams);

        if (harvested) {
            // SLOAD to memory
            uint256 donatedAssets = _donatedAssets;
            if (donatedAssets > 0) {
                _donatedAssets = 0;
                totalAssetsDelta += int256(donatedAssets);
            }
        }

        // process total assets delta if it has changed
        _processTotalAssetsDelta(totalAssetsDelta);

        // update exit queue every time new update is harvested
        if (harvested) _updateExitQueue();
    }

    /**
     * @dev Internal function for processing rewards and penalties
     * @param totalAssetsDelta The number of assets earned or lost
     */
    function _processTotalAssetsDelta(int256 totalAssetsDelta) internal virtual {
        // skip processing if there is no change in assets
        if (totalAssetsDelta == 0) return;

        // SLOAD to memory
        uint256 newTotalAssets = _totalAssets;
        if (totalAssetsDelta < 0) {
            // the delta is negative, meaning that the vault lost assets
            uint256 penalty = uint256(-totalAssetsDelta);

            // SLOAD to memory
            uint256 totalExitingAssets = _totalExitingAssets;
            if (totalExitingAssets > 0) {
                // apply penalty to exiting assets
                uint256 exitingAssetsPenalty =
                    Math.mulDiv(penalty, totalExitingAssets, totalExitingAssets + newTotalAssets);

                // apply penalty to total exiting assets
                unchecked {
                    // cannot underflow as exitingAssetsPenalty <= penalty
                    penalty -= exitingAssetsPenalty;
                }
                _totalExitingAssets = SafeCast.toUint128(totalExitingAssets - exitingAssetsPenalty);
                emit ExitingAssetsPenalized(exitingAssetsPenalty);
            }

            // subtract penalty from total assets (excludes exiting assets)
            if (penalty > 0) {
                _totalAssets = SafeCast.toUint128(newTotalAssets - penalty);
            }
            return;
        }

        // convert assets delta as it is positive
        uint256 profitAssets = uint256(totalAssetsDelta);
        newTotalAssets += profitAssets;

        // update state
        _totalAssets = SafeCast.toUint128(newTotalAssets);

        // calculate admin fee recipient assets
        uint256 feeRecipientAssets = Math.mulDiv(profitAssets, feePercent, _maxFeePercent);
        if (feeRecipientAssets == 0) return;

        // SLOAD to memory
        uint256 totalShares_ = _totalShares;

        // calculate fee recipient's shares
        uint256 feeRecipientShares;
        if (totalShares_ == 0) {
            feeRecipientShares = feeRecipientAssets;
        } else {
            unchecked {
                feeRecipientShares = Math.mulDiv(feeRecipientAssets, totalShares_, newTotalAssets - feeRecipientAssets);
            }
        }

        // SLOAD to memory
        address _feeRecipient = feeRecipient;
        // mint shares to the fee recipient
        _mintShares(_feeRecipient, feeRecipientShares);
        emit FeeSharesMinted(_feeRecipient, feeRecipientShares, feeRecipientAssets);
    }

    /**
     * @dev Internal function that must be used to process exit queue
     * @dev Make sure that sufficient time passed between exit queue updates (at least 12 hours).
     *      Currently it's restricted by the keeper's harvest interval
     * @return burnedShares The total amount of burned shares
     */
    function _updateExitQueue() internal virtual returns (uint256 burnedShares) {
        // calculate assets that can be used to process the exit requests
        uint256 availableAssets = _vaultAssets() - _unclaimedAssets;
        if (availableAssets == 0) return 0;

        // SLOAD to memory
        uint256 totalExitingAssets = _totalExitingAssets;
        if (totalExitingAssets > 0) {
            // wait for all the exiting assets from v2 to be processed
            if (availableAssets < totalExitingAssets) return 0;

            // SLOAD to memory
            uint256 totalExitingTickets = _totalExitingTickets;

            // push checkpoint so that exited assets could be claimed
            _exitQueue.push(totalExitingTickets, totalExitingAssets);
            emit CheckpointCreated(totalExitingTickets, totalExitingAssets);

            unchecked {
                // cannot underflow as _totalExitingAssets <= availableAssets
                availableAssets -= totalExitingAssets;
            }

            // update state
            _unclaimedAssets += SafeCast.toUint128(totalExitingAssets);
            _totalExitingTickets = 0;
            _totalExitingAssets = 0;
        }

        // SLOAD to memory
        uint256 queuedShares = _queuedShares;
        if (queuedShares == 0 || availableAssets == 0) return 0;

        // calculate the amount of assets that can be exited
        uint256 exitedAssets = Math.min(availableAssets, convertToAssets(queuedShares));
        if (exitedAssets == 0) return 0;

        // calculate the amount of shares that can be burned
        burnedShares = convertToShares(exitedAssets);
        if (burnedShares == 0) return 0;

        // update queued shares and unclaimed assets
        _queuedShares = SafeCast.toUint128(queuedShares - burnedShares);
        _unclaimedAssets += SafeCast.toUint128(exitedAssets);

        // push checkpoint so that exited assets could be claimed
        _exitQueue.push(burnedShares, exitedAssets);
        emit CheckpointCreated(burnedShares, exitedAssets);

        // update state
        _totalShares -= SafeCast.toUint128(burnedShares);
        _totalAssets -= SafeCast.toUint128(exitedAssets);
    }

    /**
     * @dev Internal function for minting shares
     * @param owner The address of the owner to mint shares to
     * @param shares The number of shares to mint
     */
    function _mintShares(address owner, uint256 shares) internal virtual {
        // update total shares
        _totalShares += SafeCast.toUint128(shares);

        // mint shares
        unchecked {
            // cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value
            _balances[owner] += shares;
        }
    }

    /**
     * @dev Internal function for burning shares
     * @param owner The address of the owner to burn shares for
     * @param shares The number of shares to burn
     */
    function _burnShares(address owner, uint256 shares) internal virtual {
        // burn shares
        _balances[owner] -= shares;

        // update total shares
        unchecked {
            // cannot underflow because the sum of all shares can't exceed the _totalShares
            _totalShares -= SafeCast.toUint128(shares);
        }
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view returns (uint256 shares) {
        uint256 totalShares_ = _totalShares;
        // Will revert if assets > 0, totalShares > 0 and _totalAssets = 0.
        // That corresponds to a case where any asset would represent an infinite amount of shares.
        return (assets == 0 || totalShares_ == 0) ? assets : Math.mulDiv(assets, totalShares_, _totalAssets, rounding);
    }

    /**
     * @dev Internal function for harvesting Vaults' new assets
     * @return totalAssetsDelta The total assets delta after harvest
     * @return harvested `true` when the rewards were harvested, `false` otherwise
     */
    function _harvestAssets(IKeeperRewards.HarvestParams calldata harvestParams)
        internal
        virtual
        returns (int256 totalAssetsDelta, bool harvested);

    /**
     * @dev Internal function for retrieving the total assets stored in the Vault.
     *      NB! Assets can be forcibly sent to the vault, the returned value must be used with caution
     * @return The total amount of assets stored in the Vault
     */
    function _vaultAssets() internal view virtual returns (uint256);

    /**
     * @dev Initializes the VaultState contract
     * @param capacity_ The amount after which the Vault stops accepting deposits
     */
    function __VaultState_init(uint256 capacity_) internal onlyInitializing {
        if (capacity_ == 0) revert Errors.InvalidCapacity();
        // skip setting capacity if it is unlimited
        if (capacity_ != type(uint256).max) _capacity = capacity_;
    }

    /**
     * @dev Upgrades the VaultState contract
     */
    function __VaultState_upgrade() internal onlyInitializing {
        // SLOAD to memory
        uint256 totalExitedTickets = _totalExitedTickets;
        uint256 exitQueueTicket = _exitQueue.getLatestTotalTickets();
        if (exitQueueTicket < totalExitedTickets) {
            uint256 exitedTickets;
            unchecked {
                // cannot underflow as latestTicket >= exitQueueTicket
                exitedTickets = totalExitedTickets - exitQueueTicket;
            }
            _exitQueue.push(exitedTickets, 0);
            emit CheckpointCreated(exitedTickets, 0);
        }
        _totalExitedTickets = 0;

        // SLOAD to memory
        // create checkpoint for exiting assets for uncollateralized vaults
        uint256 totalExitingAssets = _totalExitingAssets;
        if (!_isCollateralized() && totalExitingAssets > 0) {
            uint256 availableAssets = _vaultAssets() - _unclaimedAssets;
            if (availableAssets < totalExitingAssets) {
                revert Errors.InvalidAssets();
            }
            // SLOAD to memory
            uint256 totalExitingTickets = _totalExitingTickets;

            // push checkpoint so that exited assets could be claimed
            _exitQueue.push(totalExitingTickets, totalExitingAssets);
            emit CheckpointCreated(totalExitingTickets, totalExitingAssets);

            // update state
            _unclaimedAssets += SafeCast.toUint128(totalExitingAssets);
            _totalExitingTickets = 0;
            _totalExitingAssets = 0;
        }
    }

    /**
     * @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[47] private __gap;
}
VaultEnterExit.sol 215 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IVaultEnterExit} from "../../interfaces/IVaultEnterExit.sol";
import {ExitQueue} from "../../libraries/ExitQueue.sol";
import {Errors} from "../../libraries/Errors.sol";
import {VaultImmutables} from "./VaultImmutables.sol";
import {VaultState} from "./VaultState.sol";

/**
 * @title VaultEnterExit
 * @author StakeWise
 * @notice Defines the functionality for entering and exiting the Vault
 */
abstract contract VaultEnterExit is VaultImmutables, Initializable, VaultState, IVaultEnterExit {
    using ExitQueue for ExitQueue.History;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    uint256 private immutable _exitingAssetsClaimDelay;

    /**
     * @dev Constructor
     * @dev Since the immutable variable value is stored in the bytecode,
     *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
     * @param exitingAssetsClaimDelay The minimum delay after which the assets can be claimed after joining the exit queue
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(uint256 exitingAssetsClaimDelay) {
        _exitingAssetsClaimDelay = exitingAssetsClaimDelay;
    }

    /// @inheritdoc IVaultEnterExit
    function getExitQueueIndex(uint256 positionTicket) external view override returns (int256) {
        uint256 checkpointIdx = _exitQueue.getCheckpointIndex(positionTicket);
        return checkpointIdx < _exitQueue.checkpoints.length ? int256(checkpointIdx) : -1;
    }

    /// @inheritdoc IVaultEnterExit
    function enterExitQueue(uint256 shares, address receiver)
        public
        virtual
        override
        returns (uint256 positionTicket)
    {
        return _enterExitQueue(msg.sender, shares, receiver);
    }

    /// @inheritdoc IVaultEnterExit
    function calculateExitedAssets(address receiver, uint256 positionTicket, uint256 timestamp, uint256 exitQueueIndex)
        public
        view
        override
        returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets)
    {
        uint256 exitingTickets = _exitRequests[keccak256(abi.encode(receiver, timestamp, positionTicket))];
        if (exitingTickets == 0) return (0, 0, 0);

        // calculate exited tickets and assets
        (exitedTickets, exitedAssets) = _exitQueue.calculateExitedAssets(exitQueueIndex, positionTicket, exitingTickets);
        leftTickets = exitingTickets - exitedTickets;
        if (leftTickets == 1) {
            // if only one ticket is left round it to zero
            leftTickets = 0;
            exitedTickets += 1; // round up exited tickets
        }
    }

    /// @inheritdoc IVaultEnterExit
    function claimExitedAssets(uint256 positionTicket, uint256 timestamp, uint256 exitQueueIndex) external override {
        // calculate exited tickets and assets
        (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets) =
            calculateExitedAssets(msg.sender, positionTicket, timestamp, exitQueueIndex);
        if (block.timestamp < timestamp + _exitingAssetsClaimDelay || exitedTickets == 0 || exitedAssets == 0) {
            revert Errors.ExitRequestNotProcessed();
        }

        // update unclaimed assets
        _unclaimedAssets -= SafeCast.toUint128(exitedAssets);

        // clean up current exit request
        delete _exitRequests[keccak256(abi.encode(msg.sender, timestamp, positionTicket))];

        // skip creating new position for the tickets rounding error
        uint256 newPositionTicket;
        if (leftTickets > 1) {
            // update user's queue position
            newPositionTicket = positionTicket + exitedTickets;
            _exitRequests[keccak256(abi.encode(msg.sender, timestamp, newPositionTicket))] = leftTickets;
        }

        // transfer assets to the receiver
        _transferVaultAssets(msg.sender, exitedAssets);
        emit ExitedAssetsClaimed(msg.sender, positionTicket, newPositionTicket, exitedAssets);
    }

    /// @inheritdoc IVaultEnterExit
    function rescueAssets() external override {
        _checkAdmin();

        // rescue works only when the vault is not collateralized, i.e. does not have validators
        if (_isCollateralized()) {
            revert Errors.Collateralized();
        }

        // calculate amount of assets to rescue, exclude all the assets backing shares
        uint256 rescuedAssets = _vaultAssets() - _totalAssets;

        // transfer to admin
        _transferVaultAssets(msg.sender, rescuedAssets);
    }

    /**
     * @dev Internal function that must be used to process user deposits
     * @param to The address to mint shares to
     * @param assets The number of assets deposited
     * @param referrer The address of the referrer. Set to zero address if not used.
     * @return shares The total amount of shares minted
     */
    function _deposit(address to, uint256 assets, address referrer) internal virtual returns (uint256 shares) {
        _checkHarvested();
        if (to == address(0)) revert Errors.ZeroAddress();
        if (assets == 0) revert Errors.InvalidAssets();

        uint256 totalAssetsAfter;
        unchecked {
            // cannot overflow as it is capped with underlying asset total supply
            totalAssetsAfter = _totalAssets + assets;
        }
        if (totalAssetsAfter > capacity()) revert Errors.CapacityExceeded();

        // calculate amount of shares to mint
        shares = _convertToShares(assets, Math.Rounding.Ceil);

        // update state
        _totalAssets = SafeCast.toUint128(totalAssetsAfter);
        _mintShares(to, shares);

        emit Deposited(msg.sender, to, assets, shares, referrer);
    }

    /**
     * @dev Internal function for sending user shares to the exit queue
     * @param user The address of the user
     * @param shares The number of shares to send to exit queue
     * @param receiver The address that will receive the assets
     * @return positionTicket The position ticket in the exit queue. Returns max uint256 if no ticket is created.
     */
    function _enterExitQueue(address user, uint256 shares, address receiver)
        internal
        virtual
        returns (uint256 positionTicket)
    {
        if (shares == 0) revert Errors.InvalidShares();
        if (receiver == address(0)) revert Errors.ZeroAddress();
        if (!_isCollateralized()) {
            // calculate amount of assets to burn
            uint256 assets = convertToAssets(shares);
            if (assets == 0) revert Errors.InvalidAssets();

            // update total assets
            _totalAssets -= SafeCast.toUint128(assets);

            // burn owner shares
            _burnShares(user, shares);

            // transfer assets to the receiver
            _transferVaultAssets(receiver, assets);

            emit Redeemed(user, receiver, assets, shares);

            // no ticket is created, return max value
            return type(uint256).max;
        }

        // SLOAD to memory
        uint256 queuedShares = _queuedShares;

        // calculate position ticket
        positionTicket = _exitQueue.getLatestTotalTickets() + _totalExitingTickets + queuedShares;

        // add to the exit requests
        _exitRequests[keccak256(abi.encode(receiver, block.timestamp, positionTicket))] = shares;

        // reverts if owner does not have enough shares
        _balances[user] -= shares;

        unchecked {
            // cannot overflow as it is capped with _totalShares
            _queuedShares = SafeCast.toUint128(queuedShares + shares);
        }

        emit ExitQueueEntered(user, receiver, positionTicket, shares);
    }

    /**
     * @dev Internal function for transferring assets from the Vault to the receiver
     * @dev IMPORTANT: because control is transferred to the receiver, care must be
     *    taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
     *    https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
     * @param receiver The address that will receive the assets
     * @param assets The number of assets to transfer
     */
    function _transferVaultAssets(address receiver, uint256 assets) internal virtual;

    /**
     * @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[50] private __gap;
}
VaultOsToken.sol 314 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {IOsTokenVaultController} from "../../interfaces/IOsTokenVaultController.sol";
import {IOsTokenConfig} from "../../interfaces/IOsTokenConfig.sol";
import {IVaultOsToken} from "../../interfaces/IVaultOsToken.sol";
import {IOsTokenVaultEscrow} from "../../interfaces/IOsTokenVaultEscrow.sol";
import {Errors} from "../../libraries/Errors.sol";
import {VaultImmutables} from "./VaultImmutables.sol";
import {VaultEnterExit, IVaultEnterExit} from "./VaultEnterExit.sol";
import {VaultState} from "./VaultState.sol";
import {OsTokenUtils} from "../../libraries/OsTokenUtils.sol";

/**
 * @title VaultOsToken
 * @author StakeWise
 * @notice Defines the functionality for minting OsToken
 */
abstract contract VaultOsToken is VaultImmutables, VaultState, VaultEnterExit, IVaultOsToken {
    uint256 private constant _maxPercent = 1e18;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    IOsTokenVaultController private immutable _osTokenVaultController;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    IOsTokenConfig private immutable _osTokenConfig;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    IOsTokenVaultEscrow private immutable _osTokenVaultEscrow;

    mapping(address => OsTokenPosition) private _positions;

    /**
     * @dev Constructor
     * @dev Since the immutable variable value is stored in the bytecode,
     *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
     * @param osTokenVaultController The address of the OsTokenVaultController contract
     * @param osTokenConfig The address of the OsTokenConfig contract
     * @param osTokenVaultEscrow The address of the OsTokenVaultEscrow contract
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address osTokenVaultController, address osTokenConfig, address osTokenVaultEscrow) {
        _osTokenVaultController = IOsTokenVaultController(osTokenVaultController);
        _osTokenConfig = IOsTokenConfig(osTokenConfig);
        _osTokenVaultEscrow = IOsTokenVaultEscrow(osTokenVaultEscrow);
    }

    /// @inheritdoc IVaultOsToken
    function osTokenPositions(address user) public view override returns (uint128 shares) {
        OsTokenPosition memory position = _positions[user];
        if (position.shares != 0) _syncPositionFee(position);
        return position.shares;
    }

    /// @inheritdoc IVaultOsToken
    function mintOsToken(address receiver, uint256 osTokenShares, address referrer)
        public
        virtual
        override
        returns (uint256 assets)
    {
        return _mintOsToken(msg.sender, receiver, osTokenShares, referrer);
    }

    /// @inheritdoc IVaultOsToken
    function burnOsToken(uint128 osTokenShares) external override returns (uint256 assets) {
        // burn osToken shares
        assets = _osTokenVaultController.burnShares(msg.sender, osTokenShares);

        // fetch user position
        OsTokenPosition memory position = _positions[msg.sender];
        if (position.shares == 0) revert Errors.InvalidPosition();
        _syncPositionFee(position);

        // update osToken position
        position.shares -= osTokenShares;
        _positions[msg.sender] = position;

        // emit event
        emit OsTokenBurned(msg.sender, assets, osTokenShares);
    }

    /// @inheritdoc IVaultOsToken
    function liquidateOsToken(uint256 osTokenShares, address owner, address receiver) external override {
        (uint256 burnedShares, uint256 receivedAssets) = _redeemOsToken(owner, receiver, osTokenShares, true);
        emit OsTokenLiquidated(msg.sender, owner, receiver, osTokenShares, burnedShares, receivedAssets);
    }

    /// @inheritdoc IVaultOsToken
    function redeemOsToken(uint256 osTokenShares, address owner, address receiver) external override {
        if (msg.sender != _osTokenConfig.redeemer()) revert Errors.AccessDenied();
        (uint256 burnedShares, uint256 receivedAssets) = _redeemOsToken(owner, receiver, osTokenShares, false);
        emit OsTokenRedeemed(msg.sender, owner, receiver, osTokenShares, burnedShares, receivedAssets);
    }

    /// @inheritdoc IVaultOsToken
    function transferOsTokenPositionToEscrow(uint256 osTokenShares)
        external
        override
        returns (uint256 positionTicket)
    {
        // check whether vault assets are up to date
        _checkHarvested();

        // fetch user osToken position
        OsTokenPosition memory position = _positions[msg.sender];
        if (position.shares == 0) revert Errors.InvalidPosition();

        // sync accumulated fee
        _syncPositionFee(position);
        if (position.shares < osTokenShares) revert Errors.InvalidShares();

        // calculate shares to enter the exit queue
        uint256 exitShares = _balances[msg.sender];
        if (position.shares != osTokenShares) {
            // calculate exit shares
            exitShares = Math.mulDiv(exitShares, osTokenShares, position.shares);
            // update osToken position
            unchecked {
                // cannot underflow because position.shares >= osTokenShares
                position.shares -= SafeCast.toUint128(osTokenShares);
            }
            _positions[msg.sender] = position;
        } else {
            // all the assets are sent to the exit queue, remove position
            delete _positions[msg.sender];
        }

        // enter the exit queue
        positionTicket = super.enterExitQueue(exitShares, address(_osTokenVaultEscrow));

        // transfer to escrow
        _osTokenVaultEscrow.register(msg.sender, positionTicket, osTokenShares, position.cumulativeFeePerShare);
    }

    /// @inheritdoc IVaultEnterExit
    function enterExitQueue(uint256 shares, address receiver)
        public
        virtual
        override(IVaultEnterExit, VaultEnterExit)
        returns (uint256 positionTicket)
    {
        positionTicket = super.enterExitQueue(shares, receiver);
        _checkOsTokenPosition(msg.sender);
    }

    /**
     * @dev Internal function for minting osToken shares
     * @param owner The owner of the osToken position
     * @param receiver The receiver of the osToken shares
     * @param osTokenShares The amount of osToken shares to mint
     * @param referrer The address of the referrer
     * @return assets The amount of assets minted
     */
    function _mintOsToken(address owner, address receiver, uint256 osTokenShares, address referrer)
        internal
        returns (uint256 assets)
    {
        _checkCollateralized();
        _checkHarvested();

        // fetch user position
        OsTokenPosition memory position = _positions[owner];
        if (position.shares != 0) {
            _syncPositionFee(position);
        } else {
            position.cumulativeFeePerShare = SafeCast.toUint128(_osTokenVaultController.cumulativeFeePerShare());
        }

        // calculate max osToken shares that user can mint
        uint256 userMaxOsTokenShares = _calcMaxOsTokenShares(convertToAssets(_balances[owner]));
        if (osTokenShares == type(uint256).max) {
            if (userMaxOsTokenShares <= position.shares) {
                return 0;
            }
            // calculate max OsToken shares that can be minted
            unchecked {
                // cannot underflow because position.shares < userMaxOsTokenShares
                osTokenShares = userMaxOsTokenShares - position.shares;
            }
        }

        // mint osToken shares to the receiver
        assets = _osTokenVaultController.mintShares(receiver, osTokenShares);

        // add minted shares to the position
        position.shares += SafeCast.toUint128(osTokenShares);

        // calculate and validate LTV
        if (userMaxOsTokenShares < position.shares) {
            revert Errors.LowLtv();
        }

        // update state
        _positions[owner] = position;

        // emit event
        emit OsTokenMinted(owner, receiver, assets, osTokenShares, referrer);
    }

    /**
     * @dev Internal function for redeeming and liquidating osToken shares
     * @param owner The minter of the osToken shares
     * @param receiver The receiver of the assets
     * @param osTokenShares The amount of osToken shares to redeem or liquidate
     * @param isLiquidation Whether the liquidation or redemption is being performed
     * @return burnedShares The amount of shares burned
     * @return receivedAssets The amount of assets received
     */
    function _redeemOsToken(address owner, address receiver, uint256 osTokenShares, bool isLiquidation)
        private
        returns (uint256 burnedShares, uint256 receivedAssets)
    {
        if (receiver == address(0)) revert Errors.ZeroAddress();
        _checkHarvested();

        // update osToken state for gas efficiency
        _osTokenVaultController.updateState();

        // fetch user position
        OsTokenPosition memory position = _positions[owner];
        if (position.shares == 0) revert Errors.InvalidPosition();
        _syncPositionFee(position);

        // calculate received assets
        receivedAssets = OsTokenUtils.calculateReceivedAssets(
            _osTokenConfig,
            _osTokenVaultController,
            OsTokenUtils.RedemptionData({
                mintedAssets: _osTokenVaultController.convertToAssets(position.shares),
                depositedAssets: convertToAssets(_balances[owner]),
                redeemedOsTokenShares: osTokenShares,
                availableAssets: withdrawableAssets(),
                isLiquidation: isLiquidation
            })
        );

        // reduce osToken supply
        _osTokenVaultController.burnShares(msg.sender, osTokenShares);

        // update osToken position
        position.shares -= SafeCast.toUint128(osTokenShares);
        _positions[owner] = position;

        burnedShares = convertToShares(receivedAssets);

        // update total assets
        _totalAssets -= SafeCast.toUint128(receivedAssets);

        // burn owner shares
        _burnShares(owner, burnedShares);

        // transfer assets to the receiver
        _transferVaultAssets(receiver, receivedAssets);
    }

    /**
     * @dev Internal function for syncing the osToken fee
     * @param position The position to sync the fee for
     */
    function _syncPositionFee(OsTokenPosition memory position) private view {
        // fetch current cumulative fee per share
        uint256 cumulativeFeePerShare = _osTokenVaultController.cumulativeFeePerShare();

        // check whether fee is already up to date
        if (cumulativeFeePerShare == position.cumulativeFeePerShare) return;

        // add treasury fee to the position
        position.shares =
            SafeCast.toUint128(Math.mulDiv(position.shares, cumulativeFeePerShare, position.cumulativeFeePerShare));
        position.cumulativeFeePerShare = SafeCast.toUint128(cumulativeFeePerShare);
    }

    /**
     * @notice Internal function for checking position validity. Reverts if it is invalid.
     * @param user The address of the user
     */
    function _checkOsTokenPosition(address user) internal view {
        // fetch user position
        OsTokenPosition memory position = _positions[user];
        if (position.shares == 0) return;

        // check whether vault assets are up to date
        _checkHarvested();

        // sync fee
        _syncPositionFee(position);

        // calculate and validate position LTV
        if (_calcMaxOsTokenShares(convertToAssets(_balances[user])) < position.shares) {
            revert Errors.LowLtv();
        }
    }

    /**
     * @dev Internal function for calculating the maximum amount of osToken shares that can be minted
     * @param assets The amount of assets to convert to osToken shares
     * @return maxOsTokenShares The maximum amount of osToken shares that can be minted
     */
    function _calcMaxOsTokenShares(uint256 assets) internal view returns (uint256) {
        uint256 maxOsTokenAssets = Math.mulDiv(assets, _osTokenConfig.getConfig(address(this)).ltvPercent, _maxPercent);
        return _osTokenVaultController.convertToShares(maxOsTokenAssets);
    }

    /**
     * @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[50] private __gap;
}
VaultEthStaking.sol 118 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IEthValidatorsRegistry} from "../../interfaces/IEthValidatorsRegistry.sol";
import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol";
import {IVaultEthStaking} from "../../interfaces/IVaultEthStaking.sol";
import {Errors} from "../../libraries/Errors.sol";
import {ValidatorUtils} from "../../libraries/ValidatorUtils.sol";
import {VaultValidators} from "./VaultValidators.sol";
import {VaultState} from "./VaultState.sol";
import {VaultEnterExit} from "./VaultEnterExit.sol";
import {VaultMev} from "./VaultMev.sol";

/**
 * @title VaultEthStaking
 * @author StakeWise
 * @notice Defines the Ethereum staking functionality for the Vault
 */
abstract contract VaultEthStaking is
    Initializable,
    VaultState,
    VaultValidators,
    VaultEnterExit,
    VaultMev,
    IVaultEthStaking
{
    uint256 private constant _securityDeposit = 1e9;

    /// @inheritdoc IVaultEthStaking
    function deposit(address receiver, address referrer) public payable virtual override returns (uint256 shares) {
        return _deposit(receiver, msg.value, referrer);
    }

    /// @inheritdoc IVaultEthStaking
    function updateStateAndDeposit(
        address receiver,
        address referrer,
        IKeeperRewards.HarvestParams calldata harvestParams
    ) public payable virtual override returns (uint256 shares) {
        updateState(harvestParams);
        return deposit(receiver, referrer);
    }

    /**
     * @dev Function for depositing using fallback function
     */
    receive() external payable virtual {
        _deposit(msg.sender, msg.value, address(0));
    }

    /// @inheritdoc IVaultEthStaking
    function receiveFromMevEscrow() external payable override {
        if (msg.sender != mevEscrow()) revert Errors.AccessDenied();
    }

    /// @inheritdoc IVaultEthStaking
    function donateAssets() external payable override {
        _checkCollateralized();
        if (msg.value == 0) {
            revert Errors.InvalidAssets();
        }
        _donatedAssets += msg.value;
        emit AssetsDonated(msg.sender, msg.value);
    }

    /// @inheritdoc VaultValidators
    function _registerValidators(ValidatorUtils.ValidatorDeposit[] memory deposits) internal virtual override {
        uint256 depositsCount = deposits.length;
        uint256 availableAssets = withdrawableAssets();
        for (uint256 i = 0; i < depositsCount;) {
            ValidatorUtils.ValidatorDeposit memory depositData = deposits[i];
            // deposit to the validators registry
            IEthValidatorsRegistry(_validatorsRegistry).deposit{value: depositData.depositAmount}(
                depositData.publicKey,
                depositData.withdrawalCredentials,
                depositData.signature,
                depositData.depositDataRoot
            );

            // will revert if not enough assets
            availableAssets -= depositData.depositAmount;

            unchecked {
                // cannot realistically overflow
                ++i;
            }
        }
    }

    /// @inheritdoc VaultState
    function _vaultAssets() internal view virtual override returns (uint256) {
        return address(this).balance;
    }

    /// @inheritdoc VaultEnterExit
    function _transferVaultAssets(address receiver, uint256 assets) internal virtual override nonReentrant {
        return Address.sendValue(payable(receiver), assets);
    }

    /**
     * @dev Initializes the VaultEthStaking contract
     */
    function __VaultEthStaking_init() internal onlyInitializing {
        // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706
        if (msg.value < _securityDeposit) revert Errors.InvalidSecurityDeposit();
        _deposit(address(this), msg.value, address(0));
    }

    /**
     * @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[50] private __gap;
}
VaultMev.sol 80 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IKeeperRewards} from "../../interfaces/IKeeperRewards.sol";
import {ISharedMevEscrow} from "../../interfaces/ISharedMevEscrow.sol";
import {IOwnMevEscrow} from "../../interfaces/IOwnMevEscrow.sol";
import {IVaultMev} from "../../interfaces/IVaultMev.sol";
import {VaultState} from "./VaultState.sol";

/**
 * @title VaultMev
 * @author StakeWise
 * @notice Defines the Vaults' MEV functionality
 */
abstract contract VaultMev is Initializable, VaultState, IVaultMev {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _sharedMevEscrow;
    address private _ownMevEscrow;

    /**
     * @dev Constructor
     * @dev Since the immutable variable value is stored in the bytecode,
     *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
     * @param sharedMevEscrow The address of the shared MEV escrow
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address sharedMevEscrow) {
        _sharedMevEscrow = sharedMevEscrow;
    }

    /// @inheritdoc IVaultMev
    function mevEscrow() public view override returns (address) {
        // SLOAD to memory
        address ownMevEscrow = _ownMevEscrow;
        return ownMevEscrow != address(0) ? ownMevEscrow : _sharedMevEscrow;
    }

    /// @inheritdoc VaultState
    function _harvestAssets(IKeeperRewards.HarvestParams calldata harvestParams)
        internal
        virtual
        override
        returns (int256 totalAssetsDelta, bool harvested)
    {
        uint256 unlockedMevDelta;
        (totalAssetsDelta, unlockedMevDelta, harvested) = IKeeperRewards(_keeper).harvest(harvestParams);

        // harvest execution rewards only when consensus rewards were harvested
        if (!harvested) return (totalAssetsDelta, harvested);

        // SLOAD to memory
        address _mevEscrow = mevEscrow();
        if (_mevEscrow == _sharedMevEscrow) {
            if (unlockedMevDelta > 0) {
                // withdraw assets from shared escrow only in case reward is positive
                ISharedMevEscrow(_mevEscrow).harvest(unlockedMevDelta);
            }
        } else {
            // execution rewards are always equal to what was accumulated in own MEV escrow
            totalAssetsDelta += int256(IOwnMevEscrow(_mevEscrow).harvest());
        }
    }

    /**
     * @dev Initializes the VaultMev contract
     * @param ownMevEscrow The address of the own MEV escrow contract
     */
    function __VaultMev_init(address ownMevEscrow) internal onlyInitializing {
        if (ownMevEscrow != address(0)) _ownMevEscrow = ownMevEscrow;
    }

    /**
     * @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[50] private __gap;
}
IVaultAdmin.sol 42 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title IVaultState
 * @author StakeWise
 * @notice Defines the interface for the VaultAdmin contract
 */
interface IVaultAdmin {
    /**
     * @notice Event emitted on metadata ipfs hash update
     * @param caller The address of the function caller
     * @param metadataIpfsHash The new metadata IPFS hash
     */
    event MetadataUpdated(address indexed caller, string metadataIpfsHash);

    /**
     * @notice Event emitted on admin update
     * @param caller The address of the function caller
     * @param newAdmin The new admin address
     */
    event AdminUpdated(address indexed caller, address indexed newAdmin);

    /**
     * @notice The Vault admin
     * @return The address of the Vault admin
     */
    function admin() external view returns (address);

    /**
     * @notice Function for updating the metadata IPFS hash. Can only be called by Vault admin.
     * @param metadataIpfsHash The new metadata IPFS hash
     */
    function setMetadata(string calldata metadataIpfsHash) external;

    /**
     * @notice Function for updating the admin address. Can only be called by Vault current admin.
     * @param newAdmin The new admin address
     */
    function setAdmin(address newAdmin) external;
}
IVaultVersion.sol 31 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {IVaultAdmin} from "./IVaultAdmin.sol";

/**
 * @title IVaultVersion
 * @author StakeWise
 * @notice Defines the interface for VaultVersion contract
 */
interface IVaultVersion is IERC1822Proxiable, IVaultAdmin {
    /**
     * @notice Vault Unique Identifier
     * @return The unique identifier of the Vault
     */
    function vaultId() external pure returns (bytes32);

    /**
     * @notice Version
     * @return The version of the Vault implementation contract
     */
    function version() external pure returns (uint8);

    /**
     * @notice Implementation
     * @return The address of the Vault implementation contract
     */
    function implementation() external view returns (address);
}
IVaultFee.sol 50 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IVaultAdmin} from "./IVaultAdmin.sol";

/**
 * @title IVaultFee
 * @author StakeWise
 * @notice Defines the interface for the VaultFee contract
 */
interface IVaultFee is IVaultAdmin {
    /**
     * @notice Event emitted on fee recipient update
     * @param caller The address of the function caller
     * @param feeRecipient The address of the new fee recipient
     */
    event FeeRecipientUpdated(address indexed caller, address indexed feeRecipient);

    /**
     * @notice Event emitted on fee percent update
     * @param caller The address of the function caller
     * @param feePercent The new fee percent
     */
    event FeePercentUpdated(address indexed caller, uint16 feePercent);

    /**
     * @notice The Vault's fee recipient
     * @return The address of the Vault's fee recipient
     */
    function feeRecipient() external view returns (address);

    /**
     * @notice The Vault's fee percent in BPS
     * @return The fee percent applied by the Vault on the rewards
     */
    function feePercent() external view returns (uint16);

    /**
     * @notice Function for updating the fee recipient address. Can only be called by the admin.
     * @param _feeRecipient The address of the new fee recipient
     */
    function setFeeRecipient(address _feeRecipient) external;

    /**
     * @notice Function for updating the fee percent. Can only be called by the admin.
     * @param _feePercent The new fee percent
     */
    function setFeePercent(uint16 _feePercent) external;
}
IVaultState.sol 117 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperRewards} from "./IKeeperRewards.sol";
import {IVaultFee} from "./IVaultFee.sol";

/**
 * @title IVaultState
 * @author StakeWise
 * @notice Defines the interface for the VaultState contract
 */
interface IVaultState is IVaultFee {
    /**
     * @notice Event emitted on checkpoint creation
     * @param shares The number of burned shares
     * @param assets The amount of exited assets
     */
    event CheckpointCreated(uint256 shares, uint256 assets);

    /**
     * @notice Event emitted on minting fee recipient shares
     * @param receiver The address of the fee recipient
     * @param shares The number of minted shares
     * @param assets The amount of minted assets
     */
    event FeeSharesMinted(address receiver, uint256 shares, uint256 assets);

    /**
     * @notice Event emitted when exiting assets are penalized (deprecated)
     * @param penalty The total penalty amount
     */
    event ExitingAssetsPenalized(uint256 penalty);

    /**
     * @notice Event emitted when the assets are donated to the Vault
     * @param sender The address of the sender
     * @param assets The amount of donated assets
     */
    event AssetsDonated(address sender, uint256 assets);

    /**
     * @notice Total assets in the Vault
     * @return The total amount of the underlying asset that is "managed" by Vault
     */
    function totalAssets() external view returns (uint256);

    /**
     * @notice Function for retrieving total shares
     * @return The amount of shares in existence
     */
    function totalShares() external view returns (uint256);

    /**
     * @notice The Vault's capacity
     * @return The amount after which the Vault stops accepting deposits
     */
    function capacity() external view returns (uint256);

    /**
     * @notice Total assets available in the Vault. They can be staked or withdrawn.
     * @return The total amount of withdrawable assets
     */
    function withdrawableAssets() external view returns (uint256);

    /**
     * @notice Get exit queue data
     * @return queuedShares The number of shares in the exit queue
     * @return unclaimedAssets The amount of unclaimed assets in the exit queue
     * @return totalExitingTickets The total number of exiting tickets
     * @return totalExitingAssets The total amount of exiting assets
     * @return totalTickets The total number of tickets in the exit queue
     */
    function getExitQueueData()
        external
        view
        returns (
            uint128 queuedShares,
            uint128 unclaimedAssets,
            uint128 totalExitingTickets,
            uint128 totalExitingAssets,
            uint256 totalTickets
        );

    /**
     * @notice Returns the number of shares held by an account
     * @param account The account for which to look up the number of shares it has, i.e. its balance
     * @return The number of shares held by the account
     */
    function getShares(address account) external view returns (uint256);

    /**
     * @notice Converts assets to shares
     * @param assets The amount of assets to convert to shares
     * @return shares The amount of shares that the Vault would exchange for the amount of assets provided
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @notice Converts shares to assets
     * @param shares The amount of shares to convert to assets
     * @return assets The amount of assets that the Vault would exchange for the amount of shares provided
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @notice Check whether state update is required
     * @return `true` if state update is required, `false` otherwise
     */
    function isStateUpdateRequired() external view returns (bool);

    /**
     * @notice Updates the total amount of assets in the Vault and its exit queue
     * @param harvestParams The parameters for harvesting Keeper rewards
     */
    function updateState(IKeeperRewards.HarvestParams calldata harvestParams) external;
}
IVaultValidators.sol 134 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperValidators} from "./IKeeperValidators.sol";
import {IVaultAdmin} from "./IVaultAdmin.sol";
import {IVaultState} from "./IVaultState.sol";

/**
 * @title IVaultValidators
 * @author StakeWise
 * @notice Defines the interface for VaultValidators contract
 */
interface IVaultValidators is IVaultAdmin, IVaultState {
    /**
     * @notice Event emitted on V1 validator registration
     * @param publicKey The public key of the validator that was registered
     */
    event ValidatorRegistered(bytes publicKey);

    /**
     * @notice Event emitted on V2 validator registration
     * @param publicKey The public key of the validator that was registered
     * @param amount The amount of assets that was registered
     */
    event V2ValidatorRegistered(bytes publicKey, uint256 amount);

    /**
     * @notice Event emitted on validator withdrawal
     * @param publicKey The public key of the validator that was withdrawn
     * @param amount The amount of assets that was withdrawn
     * @param feePaid The amount of fee that was paid
     */
    event ValidatorWithdrawalSubmitted(bytes publicKey, uint256 amount, uint256 feePaid);

    /**
     * @notice Event emitted on validator balance top-up
     * @param publicKey The public key of the validator that was funded
     * @param amount The amount of assets that was funded
     */
    event ValidatorFunded(bytes publicKey, uint256 amount);

    /**
     * @notice Event emitted on validators consolidation
     * @param fromPublicKey The public key of the validator that was consolidated
     * @param toPublicKey The public key of the validator that was consolidated to
     * @param feePaid The amount of fee that was paid
     */
    event ValidatorConsolidationSubmitted(bytes fromPublicKey, bytes toPublicKey, uint256 feePaid);

    /**
     * @notice Event emitted on keys manager address update (deprecated)
     * @param caller The address of the function caller
     * @param keysManager The address of the new keys manager
     */
    event KeysManagerUpdated(address indexed caller, address indexed keysManager);

    /**
     * @notice Event emitted on validators merkle tree root update (deprecated)
     * @param caller The address of the function caller
     * @param validatorsRoot The new validators merkle tree root
     */
    event ValidatorsRootUpdated(address indexed caller, bytes32 indexed validatorsRoot);

    /**
     * @notice Event emitted on validators manager address update
     * @param caller The address of the function caller
     * @param validatorsManager The address of the new validators manager
     */
    event ValidatorsManagerUpdated(address indexed caller, address indexed validatorsManager);

    /**
     * @notice The Vault validators manager address
     * @return The address that can register validators
     */
    function validatorsManager() external view returns (address);

    /**
     * @notice The nonce for the validators manager used for signing
     * @return The nonce for the validators manager
     */
    function validatorsManagerNonce() external view returns (uint256);

    /**
     * @notice Function for checking if the validator is tracked V2 validator
     * @param publicKeyHash The keccak256 hash of the public key of the validator
     * @return Whether the validator is tracked V2 validator
     */
    function v2Validators(bytes32 publicKeyHash) external view returns (bool);

    /**
     * @notice Function for funding single or multiple existing validators
     * @param validators The concatenated validators data
     * @param validatorsManagerSignature The optional signature from the validators manager
     */
    function fundValidators(bytes calldata validators, bytes calldata validatorsManagerSignature) external;

    /**
     * @notice Function for withdrawing single or multiple validators
     * @param validators The concatenated validators data
     * @param validatorsManagerSignature The optional signature from the validators manager
     */
    function withdrawValidators(bytes calldata validators, bytes calldata validatorsManagerSignature)
        external
        payable;

    /**
     * @notice Function for consolidating single or multiple validators
     * @param validators The concatenated validators data
     * @param validatorsManagerSignature The optional signature from the validators manager
     * @param oracleSignatures The optional signatures from the oracles
     */
    function consolidateValidators(
        bytes calldata validators,
        bytes calldata validatorsManagerSignature,
        bytes calldata oracleSignatures
    ) external payable;

    /**
     * @notice Function for registering single or multiple validators
     * @param keeperParams The parameters for getting approval from Keeper oracles
     * @param validatorsManagerSignature The optional signature from the validators manager
     */
    function registerValidators(
        IKeeperValidators.ApprovalParams calldata keeperParams,
        bytes calldata validatorsManagerSignature
    ) external;

    /**
     * @notice Function for updating the validators manager. Can only be called by the admin. Default is the DepositDataRegistry contract.
     * @param _validatorsManager The new validators manager address
     */
    function setValidatorsManager(address _validatorsManager) external;
}
IVaultEnterExit.sol 109 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IVaultState} from "./IVaultState.sol";

/**
 * @title IVaultEnterExit
 * @author StakeWise
 * @notice Defines the interface for the VaultEnterExit contract
 */
interface IVaultEnterExit is IVaultState {
    /**
     * @notice Event emitted on deposit
     * @param caller The address that called the deposit function
     * @param receiver The address that received the shares
     * @param assets The number of assets deposited by the caller
     * @param shares The number of shares received
     * @param referrer The address of the referrer
     */
    event Deposited(address indexed caller, address indexed receiver, uint256 assets, uint256 shares, address referrer);

    /**
     * @notice Event emitted on redeem
     * @param owner The address that owns the shares
     * @param receiver The address that received withdrawn assets
     * @param assets The total number of withdrawn assets
     * @param shares The total number of withdrawn shares
     */
    event Redeemed(address indexed owner, address indexed receiver, uint256 assets, uint256 shares);

    /**
     * @notice Event emitted on shares added to the exit queue
     * @param owner The address that owns the shares
     * @param receiver The address that will receive withdrawn assets
     * @param positionTicket The exit queue ticket that was assigned to the position
     * @param shares The number of shares that queued for the exit
     */
    event ExitQueueEntered(address indexed owner, address indexed receiver, uint256 positionTicket, uint256 shares);

    /**
     * @notice Event emitted on shares added to the V2 exit queue (deprecated)
     * @param owner The address that owns the shares
     * @param receiver The address that will receive withdrawn assets
     * @param positionTicket The exit queue ticket that was assigned to the position
     * @param shares The number of shares that queued for the exit
     * @param assets The number of assets that queued for the exit
     */
    event V2ExitQueueEntered(
        address indexed owner, address indexed receiver, uint256 positionTicket, uint256 shares, uint256 assets
    );

    /**
     * @notice Event emitted on claim of the exited assets
     * @param receiver The address that has received withdrawn assets
     * @param prevPositionTicket The exit queue ticket received after the `enterExitQueue` call
     * @param newPositionTicket The new exit queue ticket in case not all the shares were withdrawn. Otherwise 0.
     * @param withdrawnAssets The total number of assets withdrawn
     */
    event ExitedAssetsClaimed(
        address indexed receiver, uint256 prevPositionTicket, uint256 newPositionTicket, uint256 withdrawnAssets
    );

    /**
     * @notice Locks shares to the exit queue. The shares continue earning rewards until they will be burned by the Vault.
     * @param shares The number of shares to lock
     * @param receiver The address that will receive assets upon withdrawal
     * @return positionTicket The position ticket of the exit queue. Returns uint256 max if no ticket created.
     */
    function enterExitQueue(uint256 shares, address receiver) external returns (uint256 positionTicket);

    /**
     * @notice Get the exit queue index to claim exited assets from
     * @param positionTicket The exit queue position ticket to get the index for
     * @return The exit queue index that should be used to claim exited assets.
     *         Returns -1 in case such index does not exist.
     */
    function getExitQueueIndex(uint256 positionTicket) external view returns (int256);

    /**
     * @notice Calculates the number of shares and assets that can be claimed from the exit queue.
     * @param receiver The address that will receive assets upon withdrawal
     * @param positionTicket The exit queue ticket received after the `enterExitQueue` call
     * @param timestamp The timestamp when the shares entered the exit queue
     * @param exitQueueIndex The exit queue index at which the shares were burned. It can be looked up by calling `getExitQueueIndex`.
     * @return leftTickets The number of tickets left in the queue
     * @return exitedTickets The number of tickets that have already exited
     * @return exitedAssets The number of assets that can be claimed
     */
    function calculateExitedAssets(address receiver, uint256 positionTicket, uint256 timestamp, uint256 exitQueueIndex)
        external
        view
        returns (uint256 leftTickets, uint256 exitedTickets, uint256 exitedAssets);

    /**
     * @notice Claims assets that were withdrawn by the Vault. It can be called only after the `enterExitQueue` call by the `receiver`.
     * @param positionTicket The exit queue ticket received after the `enterExitQueue` call
     * @param timestamp The timestamp when the assets entered the exit queue
     * @param exitQueueIndex The exit queue index at which the shares were burned.
     *        It can be looked up by calling `getExitQueueIndex`.
     */
    function claimExitedAssets(uint256 positionTicket, uint256 timestamp, uint256 exitQueueIndex) external;

    /**
     * @notice Rescue any assets that are not backing shares when the vault is not collateralized.
     * @dev Can be called only by the admin when the vault is not collateralized.
     */
    function rescueAssets() external;
}
IVaultOsToken.sol 124 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IVaultState} from "./IVaultState.sol";
import {IVaultEnterExit} from "./IVaultEnterExit.sol";

/**
 * @title IVaultOsToken
 * @author StakeWise
 * @notice Defines the interface for the VaultOsToken contract
 */
interface IVaultOsToken is IVaultState, IVaultEnterExit {
    /**
     * @notice Event emitted on minting osToken
     * @param caller The address of the function caller
     * @param receiver The address of the osToken receiver
     * @param assets The amount of minted assets
     * @param shares The amount of minted shares
     * @param referrer The address of the referrer
     */
    event OsTokenMinted(address indexed caller, address receiver, uint256 assets, uint256 shares, address referrer);

    /**
     * @notice Event emitted on burning OsToken
     * @param caller The address of the function caller
     * @param assets The amount of burned assets
     * @param shares The amount of burned shares
     */
    event OsTokenBurned(address indexed caller, uint256 assets, uint256 shares);

    /**
     * @notice Event emitted on osToken position liquidation
     * @param caller The address of the function caller
     * @param user The address of the user liquidated
     * @param receiver The address of the receiver of the liquidated assets
     * @param osTokenShares The amount of osToken shares to liquidate
     * @param shares The amount of vault shares burned
     * @param receivedAssets The amount of assets received
     */
    event OsTokenLiquidated(
        address indexed caller,
        address indexed user,
        address receiver,
        uint256 osTokenShares,
        uint256 shares,
        uint256 receivedAssets
    );

    /**
     * @notice Event emitted on osToken position redemption
     * @param caller The address of the function caller
     * @param user The address of the position owner to redeem from
     * @param receiver The address of the receiver of the redeemed assets
     * @param osTokenShares The amount of osToken shares to redeem
     * @param shares The amount of vault shares burned
     * @param assets The amount of assets received
     */
    event OsTokenRedeemed(
        address indexed caller,
        address indexed user,
        address receiver,
        uint256 osTokenShares,
        uint256 shares,
        uint256 assets
    );

    /**
     * @notice Struct of osToken position
     * @param shares The total number of minted osToken shares. Will increase based on the treasury fee.
     * @param cumulativeFeePerShare The cumulative fee per share
     */
    struct OsTokenPosition {
        uint128 shares;
        uint128 cumulativeFeePerShare;
    }

    /**
     * @notice Get total amount of minted osToken shares
     * @param user The address of the user
     * @return shares The number of minted osToken shares
     */
    function osTokenPositions(address user) external view returns (uint128 shares);

    /**
     * @notice Mints OsToken shares
     * @param receiver The address that will receive the minted OsToken shares
     * @param osTokenShares The number of OsToken shares to mint to the receiver. To mint the maximum amount of shares, use 2^256 - 1.
     * @param referrer The address of the referrer
     * @return assets The number of assets minted to the receiver
     */
    function mintOsToken(address receiver, uint256 osTokenShares, address referrer) external returns (uint256 assets);

    /**
     * @notice Burns osToken shares
     * @param osTokenShares The number of shares to burn
     * @return assets The number of assets burned
     */
    function burnOsToken(uint128 osTokenShares) external returns (uint256 assets);

    /**
     * @notice Liquidates a user position and returns the number of received assets.
     *         Can only be called when health factor is below 1 by the liquidator.
     * @param osTokenShares The number of shares to cover
     * @param owner The address of the position owner to liquidate
     * @param receiver The address of the receiver of the liquidated assets
     */
    function liquidateOsToken(uint256 osTokenShares, address owner, address receiver) external;

    /**
     * @notice Redeems osToken shares for assets. Can only be called when health factor is above redeemFromHealthFactor by the redeemer.
     * @param osTokenShares The number of osToken shares to redeem
     * @param owner The address of the position owner to redeem from
     * @param receiver The address of the receiver of the redeemed assets
     */
    function redeemOsToken(uint256 osTokenShares, address owner, address receiver) external;

    /**
     * @notice Transfers minted osToken shares to the OsTokenVaultEscrow contract, enters the exit queue for staked assets
     * @param osTokenShares The number of osToken shares to transfer
     * @return positionTicket The exit position ticket
     */
    function transferOsTokenPositionToEscrow(uint256 osTokenShares) external returns (uint256 positionTicket);
}
IVaultMev.sol 18 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IVaultState} from "./IVaultState.sol";

/**
 * @title IVaultMev
 * @author StakeWise
 * @notice Common interface for the VaultMev contracts
 */
interface IVaultMev is IVaultState {
    /**
     * @notice The contract that accumulates MEV rewards
     * @return The MEV escrow contract address
     */
    function mevEscrow() external view returns (address);
}
IVaultEthStaking.sol 47 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IVaultState} from "./IVaultState.sol";
import {IVaultValidators} from "./IVaultValidators.sol";
import {IVaultEnterExit} from "./IVaultEnterExit.sol";
import {IKeeperRewards} from "./IKeeperRewards.sol";
import {IVaultMev} from "./IVaultMev.sol";

/**
 * @title IVaultEthStaking
 * @author StakeWise
 * @notice Defines the interface for the VaultEthStaking contract
 */
interface IVaultEthStaking is IVaultState, IVaultValidators, IVaultEnterExit, IVaultMev {
    /**
     * @notice Deposit ETH to the Vault
     * @param receiver The address that will receive Vault's shares
     * @param referrer The address of the referrer. Set to zero address if not used.
     * @return shares The number of shares minted
     */
    function deposit(address receiver, address referrer) external payable returns (uint256 shares);

    /**
     * @notice Used by MEV escrow to transfer ETH.
     */
    function receiveFromMevEscrow() external payable;

    /**
     * @notice Donate assets to the Vault. Must transfer ETH together with the call.
     */
    function donateAssets() external payable;

    /**
     * @notice Updates Vault state and deposits ETH to the Vault
     * @param receiver The address that will receive Vault's shares
     * @param referrer The address of the referrer. Set to zero address if not used.
     * @param harvestParams The parameters for harvesting Keeper rewards
     * @return shares The number of shares minted
     */
    function updateStateAndDeposit(
        address receiver,
        address referrer,
        IKeeperRewards.HarvestParams calldata harvestParams
    ) external payable returns (uint256 shares);
}
IMulticall.sol 18 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.22;

/**
 * @title Multicall
 * @author Uniswap
 * @notice Adopted from https://github.com/Uniswap/v3-periphery/blob/1d69caf0d6c8cfeae9acd1f34ead30018d6e6400/contracts/base/Multicall.sol
 * @notice Enables calling multiple methods in a single call to the contract
 */
interface IMulticall {
    /**
     * @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
     * @param data The encoded function data for each of the calls to make to this contract
     * @return results The results from each of the calls passed in via data
     */
    function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}
IKeeperOracles.sol 61 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";

/**
 * @title IKeeperOracles
 * @author StakeWise
 * @notice Defines the interface for the KeeperOracles contract
 */
interface IKeeperOracles is IERC5267 {
    /**
     * @notice Event emitted on the oracle addition
     * @param oracle The address of the added oracle
     */
    event OracleAdded(address indexed oracle);

    /**
     * @notice Event emitted on the oracle removal
     * @param oracle The address of the removed oracle
     */
    event OracleRemoved(address indexed oracle);

    /**
     * @notice Event emitted on oracles config update
     * @param configIpfsHash The IPFS hash of the new config
     */
    event ConfigUpdated(string configIpfsHash);

    /**
     * @notice Function for verifying whether oracle is registered or not
     * @param oracle The address of the oracle to check
     * @return `true` for the registered oracle, `false` otherwise
     */
    function isOracle(address oracle) external view returns (bool);

    /**
     * @notice Total Oracles
     * @return The total number of oracles registered
     */
    function totalOracles() external view returns (uint256);

    /**
     * @notice Function for adding oracle to the set
     * @param oracle The address of the oracle to add
     */
    function addOracle(address oracle) external;

    /**
     * @notice Function for removing oracle from the set
     * @param oracle The address of the oracle to remove
     */
    function removeOracle(address oracle) external;

    /**
     * @notice Function for updating the config IPFS hash
     * @param configIpfsHash The new config IPFS hash
     */
    function updateConfig(string calldata configIpfsHash) external;
}
Address.sol 150 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

        (bool success, bytes memory returndata) = recipient.call{value: amount}("");
        if (!success) {
            _revert(returndata);
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}
ReentrancyGuardUpgradeable.sol 108 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;
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 EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * 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;

    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
    struct ReentrancyGuardStorage {
        uint256 _status;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
        assembly {
            $.slot := ReentrancyGuardStorageLocation
        }
    }

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

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

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        $._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 {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if ($._status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        $._status = ENTERED;
    }

    function _nonReentrantAfter() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // 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) {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        return $._status == ENTERED;
    }
}
IKeeperValidators.sol 92 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperRewards} from "./IKeeperRewards.sol";
import {IKeeperOracles} from "./IKeeperOracles.sol";

/**
 * @title IKeeperValidators
 * @author StakeWise
 * @notice Defines the interface for the Keeper validators
 */
interface IKeeperValidators is IKeeperOracles, IKeeperRewards {
    /**
     * @notice Event emitted on validators approval
     * @param vault The address of the Vault
     * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
     */
    event ValidatorsApproval(address indexed vault, string exitSignaturesIpfsHash);

    /**
     * @notice Event emitted on exit signatures update
     * @param caller The address of the function caller
     * @param vault The address of the Vault
     * @param nonce The nonce used for verifying Oracles' signatures
     * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
     */
    event ExitSignaturesUpdated(
        address indexed caller, address indexed vault, uint256 nonce, string exitSignaturesIpfsHash
    );

    /**
     * @notice Event emitted on validators min oracles number update
     * @param oracles The new minimum number of oracles required to approve validators
     */
    event ValidatorsMinOraclesUpdated(uint256 oracles);

    /**
     * @notice Get nonce for the next vault exit signatures update
     * @param vault The address of the Vault to get the nonce for
     * @return The nonce of the Vault for updating signatures
     */
    function exitSignaturesNonces(address vault) external view returns (uint256);

    /**
     * @notice Struct for approving registration of one or more validators
     * @param validatorsRegistryRoot The deposit data root used to verify that oracles approved validators
     * @param deadline The deadline for submitting the approval
     * @param validators The concatenation of the validators' public key, signature and deposit data root
     * @param signatures The concatenation of Oracles' signatures
     * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
     */
    struct ApprovalParams {
        bytes32 validatorsRegistryRoot;
        uint256 deadline;
        bytes validators;
        bytes signatures;
        string exitSignaturesIpfsHash;
    }

    /**
     * @notice The minimum number of oracles required to update validators
     * @return The minimum number of oracles
     */
    function validatorsMinOracles() external view returns (uint256);

    /**
     * @notice Function for approving validators registration
     * @param params The parameters for approving validators registration
     */
    function approveValidators(ApprovalParams calldata params) external;

    /**
     * @notice Function for updating exit signatures for every hard fork
     * @param vault The address of the Vault to update signatures for
     * @param deadline The deadline for submitting signatures update
     * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
     * @param oraclesSignatures The concatenation of Oracles' signatures
     */
    function updateExitSignatures(
        address vault,
        uint256 deadline,
        string calldata exitSignaturesIpfsHash,
        bytes calldata oraclesSignatures
    ) external;

    /**
     * @notice Function for updating validators min oracles number
     * @param _validatorsMinOracles The new minimum number of oracles required to approve validators
     */
    function setValidatorsMinOracles(uint256 _validatorsMinOracles) external;
}
IConsolidationsChecker.sol 31 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";

/**
 * @title IConsolidationsChecker
 * @author StakeWise
 * @notice Defines the interface for the ConsolidationsChecker contract
 */
interface IConsolidationsChecker is IERC5267 {
    /**
     * @notice Verifies the signatures of oracles for validators consolidations. Reverts if the signatures are invalid.
     * @param vault The address of the vault
     * @param validators The concatenation of the validators' data
     * @param signatures The concatenation of the oracles' signatures
     */
    function verifySignatures(address vault, bytes calldata validators, bytes calldata signatures) external;

    /**
     * @notice Function for checking signatures of oracles for validators consolidations
     * @param vault The address of the vault
     * @param validators The concatenation of the validators' data
     * @param signatures The concatenation of the oracles' signatures
     * @return `true` if the signatures are valid, `false` otherwise
     */
    function isValidSignatures(address vault, bytes calldata validators, bytes calldata signatures)
        external
        returns (bool);
}
IDepositDataRegistry.sol 117 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IKeeperValidators} from "./IKeeperValidators.sol";
import {IKeeperRewards} from "./IKeeperRewards.sol";
import {IMulticall} from "./IMulticall.sol";

/**
 * @title IDepositDataRegistry
 * @author StakeWise
 * @notice Defines the interface for DepositDataRegistry
 */
interface IDepositDataRegistry is IMulticall {
    /**
     * @notice Event emitted on deposit data manager update
     * @param vault The address of the vault
     * @param depositDataManager The address of the new deposit data manager
     */
    event DepositDataManagerUpdated(address indexed vault, address depositDataManager);

    /**
     * @notice Event emitted on deposit data root update
     * @param vault The address of the vault
     * @param depositDataRoot The new deposit data Merkle tree root
     */
    event DepositDataRootUpdated(address indexed vault, bytes32 depositDataRoot);

    /**
     * @notice Event emitted on deposit data migration
     * @param vault The address of the vault
     * @param depositDataRoot The deposit data root
     * @param validatorIndex The index of the next validator to be registered
     * @param depositDataManager The address of the deposit data manager
     */
    event DepositDataMigrated(
        address indexed vault, bytes32 depositDataRoot, uint256 validatorIndex, address depositDataManager
    );

    /**
     * @notice The vault deposit data index
     * @param vault The address of the vault
     * @return validatorIndex The index of the next validator to be registered
     */
    function depositDataIndexes(address vault) external view returns (uint256 validatorIndex);

    /**
     * @notice The vault deposit data root
     * @param vault The address of the vault
     * @return depositDataRoot The deposit data root
     */
    function depositDataRoots(address vault) external view returns (bytes32 depositDataRoot);

    /**
     * @notice The vault deposit data manager. Defaults to the vault admin if not set.
     * @param vault The address of the vault
     * @return depositDataManager The address of the deposit data manager
     */
    function getDepositDataManager(address vault) external view returns (address);

    /**
     * @notice Function for setting the deposit data manager for the vault. Can only be called by the vault admin.
     * @param vault The address of the vault
     * @param depositDataManager The address of the new deposit data manager
     */
    function setDepositDataManager(address vault, address depositDataManager) external;

    /**
     * @notice Function for setting the deposit data root for the vault. Can only be called by the deposit data manager.
     * @param vault The address of the vault
     * @param depositDataRoot The new deposit data Merkle tree root
     */
    function setDepositDataRoot(address vault, bytes32 depositDataRoot) external;

    /**
     * @notice Updates the vault state. Can be used in multicall to update state and register validator(s).
     * @param vault The address of the vault
     * @param harvestParams The harvest params to use for updating the vault state
     */
    function updateVaultState(address vault, IKeeperRewards.HarvestParams calldata harvestParams) external;

    /**
     * @notice Function for registering single validator
     * @param vault The address of the vault
     * @param keeperParams The parameters for getting approval from Keeper oracles
     * @param proof The proof used to verify that the validator is part of the deposit data merkle tree
     */
    function registerValidator(
        address vault,
        IKeeperValidators.ApprovalParams calldata keeperParams,
        bytes32[] calldata proof
    ) external;

    /**
     * @notice Function for registering multiple validators
     * @param vault The address of the vault
     * @param keeperParams The parameters for getting approval from Keeper oracles
     * @param indexes The indexes of the leaves for the merkle tree multi proof verification
     * @param proofFlags The multi proof flags for the merkle tree verification
     * @param proof The proof used for the merkle tree verification
     */
    function registerValidators(
        address vault,
        IKeeperValidators.ApprovalParams calldata keeperParams,
        uint256[] calldata indexes,
        bool[] calldata proofFlags,
        bytes32[] calldata proof
    ) external;

    /**
     * @notice Function for migrating the deposit data of the Vault. Can only be called once by a vault during an upgrade.
     * @param depositDataRoot The current deposit data root
     * @param validatorIndex The current index of the next validator to be registered
     * @param depositDataManager The address of the deposit data manager
     */
    function migrate(bytes32 depositDataRoot, uint256 validatorIndex, address depositDataManager) external;
}
Errors.sol 66 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title Errors
 * @author StakeWise
 * @notice Contains all the custom errors
 */
library Errors {
    error AccessDenied();
    error InvalidShares();
    error InvalidAssets();
    error ZeroAddress();
    error CapacityExceeded();
    error InvalidCapacity();
    error InvalidSecurityDeposit();
    error InvalidFeeRecipient();
    error InvalidFeePercent();
    error NotHarvested();
    error NotCollateralized();
    error Collateralized();
    error InvalidProof();
    error LowLtv();
    error InvalidPosition();
    error InvalidHealthFactor();
    error InvalidReceivedAssets();
    error InvalidTokenMeta();
    error UpgradeFailed();
    error InvalidValidators();
    error DeadlineExpired();
    error PermitInvalidSigner();
    error InvalidValidatorsRegistryRoot();
    error InvalidVault();
    error AlreadyAdded();
    error AlreadyRemoved();
    error InvalidOracles();
    error NotEnoughSignatures();
    error InvalidOracle();
    error TooEarlyUpdate();
    error InvalidAvgRewardPerSecond();
    error InvalidRewardsRoot();
    error HarvestFailed();
    error LiquidationDisabled();
    error InvalidLiqThresholdPercent();
    error InvalidLiqBonusPercent();
    error InvalidLtvPercent();
    error InvalidCheckpointIndex();
    error InvalidCheckpointValue();
    error MaxOraclesExceeded();
    error ExitRequestNotProcessed();
    error ValueNotChanged();
    error FlashLoanFailed();
    error CannotTopUpV1Validators();
    error InvalidSignatures();
    error EmptySubVaults();
    error EjectingVaultNotFound();
    error EjectingVault();
    error RepeatedEjectingVault();
    error UnclaimedAssets();
    error InvalidCurator();
    error RewardsNonceIsHigher();
    error InvalidRedeemablePositions();
    error RedeemablePositionsProposed();
    error InvalidDelay();
}
ValidatorUtils.sol 297 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {IVaultValidators} from "../interfaces/IVaultValidators.sol";
import {Errors} from "./Errors.sol";

/**
 * @title ValidatorUtils
 * @author StakeWise
 * @notice Includes functionality for managing the validators
 */
library ValidatorUtils {
    bytes32 private constant _validatorsManagerTypeHash =
        keccak256("VaultValidators(bytes32 validatorsRegistryRoot,bytes validators)");
    uint256 private constant _validatorV1DepositLength = 176;
    uint256 private constant _validatorV2DepositLength = 184;
    uint256 private constant _validatorWithdrawalLength = 56;
    uint256 private constant _validatorConsolidationLength = 96;
    uint256 private constant _validatorMinEffectiveBalance = 32 ether;
    uint256 private constant _validatorMaxEffectiveBalance = 2048 ether;

    /*
    * @dev Struct to hold the validator registration data
    * @param publicKey The public key of the validator
    * @param signature The signature of the validator
    * @param withdrawalCredentials The withdrawal credentials of the validator
    * @param depositDataRoot The deposit data root of the validator
    * @param depositAmount The deposit amount of the validator
    */
    struct ValidatorDeposit {
        bytes publicKey;
        bytes signature;
        bytes withdrawalCredentials;
        bytes32 depositDataRoot;
        uint256 depositAmount;
    }

    /**
     * @dev Function to check if the validator signature is valid
     * @param nonce The nonce of the validator
     * @param domainSeparator The domain separator of the validator
     * @param validatorsManager The address of the validators manager
     * @param validators The validators data
     * @param signature The signature of the validator
     * @return Whether the signature is valid
     */
    function isValidManagerSignature(
        bytes32 nonce,
        bytes32 domainSeparator,
        address validatorsManager,
        bytes calldata validators,
        bytes calldata signature
    ) external view returns (bool) {
        bytes32 messageHash = MessageHashUtils.toTypedDataHash(
            domainSeparator, keccak256(abi.encode(_validatorsManagerTypeHash, nonce, keccak256(validators)))
        );
        return SignatureChecker.isValidSignatureNow(validatorsManager, messageHash, signature);
    }

    /**
     * @dev Function to get the validator registration data
     * @param validator The validator data
     * @param isV1Validator Whether the validator is a V1 validator
     * @return validatorDeposit The validator registration data
     */
    function getValidatorDeposit(bytes calldata validator, bool isV1Validator)
        internal
        view
        returns (ValidatorDeposit memory validatorDeposit)
    {
        validatorDeposit.publicKey = validator[:48];
        validatorDeposit.signature = validator[48:144];
        validatorDeposit.depositDataRoot = bytes32(validator[144:176]);

        // get the deposit amount and withdrawal credentials prefix
        bytes1 withdrawalCredsPrefix;
        if (isV1Validator) {
            withdrawalCredsPrefix = 0x01;
            validatorDeposit.depositAmount = _validatorMinEffectiveBalance;
        } else {
            withdrawalCredsPrefix = 0x02;
            // extract amount from data, convert gwei to wei by multiplying by 1 gwei
            validatorDeposit.depositAmount = (uint256(uint64(bytes8(validator[176:184]))) * 1 gwei);
        }
        validatorDeposit.withdrawalCredentials = abi.encodePacked(withdrawalCredsPrefix, bytes11(0x0), address(this));
    }

    /**
     * @dev Function to get the type of validators
     * @param validatorsLength The length of the validators data
     * @return isV1Validators Whether the validators are V1 validators
     */
    function getIsV1Validators(uint256 validatorsLength) internal pure returns (bool) {
        bool isV1Validators = validatorsLength % _validatorV1DepositLength == 0;
        bool isV2Validators = validatorsLength % _validatorV2DepositLength == 0;
        if (validatorsLength == 0 || (isV1Validators && isV2Validators) || (!isV1Validators && !isV2Validators)) {
            revert Errors.InvalidValidators();
        }

        return isV1Validators;
    }

    /**
     * @dev Function to get the validator registrations
     * @param v2Validators The mapping of public key hashes to registration status
     * @param validators The validators data
     * @param isTopUp Whether the registration is a top-up
     * @return validatorDeposits The array of validator registrations
     */
    function getValidatorDeposits(
        mapping(bytes32 publicKeyHash => bool isRegistered) storage v2Validators,
        bytes calldata validators,
        bool isTopUp
    ) external returns (ValidatorDeposit[] memory validatorDeposits) {
        // check validators length is valid
        uint256 validatorsLength = validators.length;
        bool isV1Validators = getIsV1Validators(validatorsLength);

        // top up is only allowed for V2 validators
        if (isTopUp && isV1Validators) {
            revert Errors.CannotTopUpV1Validators();
        }

        uint256 _validatorDepositLength = (isV1Validators ? _validatorV1DepositLength : _validatorV2DepositLength);
        uint256 validatorsCount = validatorsLength / _validatorDepositLength;

        uint256 startIndex;
        validatorDeposits = new ValidatorDeposit[](validatorsCount);
        for (uint256 i = 0; i < validatorsCount;) {
            ValidatorDeposit memory valDeposit =
                getValidatorDeposit(validators[startIndex:startIndex + _validatorDepositLength], isV1Validators);

            if (isTopUp) {
                // check whether validator is tracked in case of the top-up
                if (!v2Validators[keccak256(valDeposit.publicKey)]) {
                    revert Errors.InvalidValidators();
                }
                // add registration data to the array
                validatorDeposits[i] = valDeposit;
                emit IVaultValidators.ValidatorFunded(valDeposit.publicKey, valDeposit.depositAmount);
                unchecked {
                    // cannot realistically overflow
                    ++i;
                    startIndex += _validatorDepositLength;
                }
                continue;
            }

            // check the registration amount
            if (
                valDeposit.depositAmount > _validatorMaxEffectiveBalance
                    || valDeposit.depositAmount < _validatorMinEffectiveBalance
            ) {
                revert Errors.InvalidAssets();
            }

            // mark v2 validator public key as tracked
            if (isV1Validators) {
                emit IVaultValidators.ValidatorRegistered(valDeposit.publicKey);
            } else {
                v2Validators[keccak256(valDeposit.publicKey)] = true;
                emit IVaultValidators.V2ValidatorRegistered(valDeposit.publicKey, valDeposit.depositAmount);
            }

            // add registration data to the array
            validatorDeposits[i] = valDeposit;

            unchecked {
                // cannot realistically overflow
                ++i;
                startIndex += _validatorDepositLength;
            }
        }
    }

    /**
     * @dev Function to withdraw the validators
     * @param validators The validators data
     * @param validatorsWithdrawals The address of the validators withdrawals contract
     */
    function withdrawValidators(bytes calldata validators, address validatorsWithdrawals) external {
        // check validators length is valid
        uint256 validatorsCount = validators.length / _validatorWithdrawalLength;
        unchecked {
            if (validatorsCount == 0 || validators.length % _validatorWithdrawalLength != 0) {
                revert Errors.InvalidValidators();
            }
        }

        uint256 startIndex;
        uint256 totalFeeAssets = msg.value;
        for (uint256 i = 0; i < validatorsCount;) {
            bytes calldata validator = validators[startIndex:startIndex + _validatorWithdrawalLength];
            bytes calldata publicKey = validator[:48];

            // convert gwei to wei by multiplying by 1 gwei
            uint256 withdrawnAmount = (uint256(uint64(bytes8(validator[48:56]))) * 1 gwei);
            uint256 feePaid = uint256(bytes32(Address.functionStaticCall(validatorsWithdrawals, "")));

            // submit validator withdrawal
            Address.functionCallWithValue(validatorsWithdrawals, validator, feePaid);
            totalFeeAssets -= feePaid;
            emit IVaultValidators.ValidatorWithdrawalSubmitted(publicKey, withdrawnAmount, feePaid);

            unchecked {
                // cannot realistically overflow
                ++i;
                startIndex += _validatorWithdrawalLength;
            }
        }

        // send the remaining assets to the caller
        if (totalFeeAssets > 0) {
            Address.sendValue(payable(msg.sender), totalFeeAssets);
        }
    }

    /**
     * @dev Internal function for consolidating validators
     * @param validator The validator data
     * @param validatorsConsolidations The address of the validators consolidations contract
     * @param fromPublicKey The public key of the validator that was consolidated
     * @param toPublicKey The public key of the validator that was consolidated to
     * @param feePaid The amount of fee that was paid
     */
    function consolidateValidator(bytes calldata validator, address validatorsConsolidations)
        internal
        returns (bytes calldata fromPublicKey, bytes calldata toPublicKey, uint256 feePaid)
    {
        fromPublicKey = validator[:48];
        toPublicKey = validator[48:96];
        feePaid = uint256(bytes32(Address.functionStaticCall(validatorsConsolidations, "")));

        Address.functionCallWithValue(validatorsConsolidations, validator, feePaid);
    }

    /**
     * @dev Function to consolidate the validators
     * @param v2Validators The mapping of public key hashes to registration status
     * @param validators The validators data
     * @param consolidationsApproved Whether the consolidations are approved
     * @param validatorsConsolidations The address of the validators consolidations contract
     */
    function consolidateValidators(
        mapping(bytes32 publicKeyHash => bool isRegistered) storage v2Validators,
        bytes calldata validators,
        bool consolidationsApproved,
        address validatorsConsolidations
    ) external {
        // Check validators length is valid
        uint256 validatorsCount = validators.length / _validatorConsolidationLength;
        unchecked {
            if (validatorsCount == 0 || validators.length % _validatorConsolidationLength != 0) {
                revert Errors.InvalidValidators();
            }
        }

        uint256 totalFeeAssets = msg.value;

        // Process each validator
        uint256 startIndex;
        for (uint256 i = 0; i < validatorsCount;) {
            // consolidate validators
            (bytes calldata sourcePublicKey, bytes calldata destPublicKey, uint256 feePaid) = consolidateValidator(
                validators[startIndex:startIndex + _validatorConsolidationLength], validatorsConsolidations
            );

            // check whether the destination public key is tracked or approved
            bytes32 destPubKeyHash = keccak256(destPublicKey);
            if (consolidationsApproved) {
                v2Validators[destPubKeyHash] = true;
            } else if (!v2Validators[destPubKeyHash]) {
                revert Errors.InvalidValidators();
            }

            // Update fees and emit event
            unchecked {
                // cannot realistically overflow
                totalFeeAssets -= feePaid;
                startIndex += _validatorConsolidationLength;
                ++i;
            }

            // emit event
            emit IVaultValidators.ValidatorConsolidationSubmitted(sourcePublicKey, destPublicKey, feePaid);
        }

        // refund unused fees
        if (totalFeeAssets > 0) {
            Address.sendValue(payable(msg.sender), totalFeeAssets);
        }
    }
}
EIP712Utils.sol 27 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title EIP712Utils
 * @author StakeWise
 * @notice Includes functionality for calculating EIP712 hashes
 */
library EIP712Utils {
    bytes32 private constant _domainTypeHash =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    bytes32 private constant _versionHash = keccak256("1");

    /**
     * @notice Computes the hash of the EIP712 typed data
     * @dev This function is used to compute the hash of the EIP712 typed data
     * @param name The name of the domain
     * @param verifyingContract The address of the verifying contract
     * @return The hash of the EIP712 typed data
     */
    function computeDomainSeparator(string memory name, address verifyingContract) external view returns (bytes32) {
        return keccak256(
            abi.encode(_domainTypeHash, keccak256(bytes(name)), _versionHash, block.chainid, verifyingContract)
        );
    }
}
UUPSUpgradeable.sol 152 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
ERC1967Utils.sol 177 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.22;

import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This library provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
 */
library ERC1967Utils {
    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit IERC1967.Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit IERC1967.AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the ERC-1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit IERC1967.BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}
IVaultsRegistry.sol 98 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title IVaultsRegistry
 * @author StakeWise
 * @notice Defines the interface for the VaultsRegistry
 */
interface IVaultsRegistry {
    /**
     * @notice Event emitted on a Vault addition
     * @param caller The address that has added the Vault
     * @param vault The address of the added Vault
     */
    event VaultAdded(address indexed caller, address indexed vault);

    /**
     * @notice Event emitted on adding Vault implementation contract
     * @param impl The address of the new implementation contract
     */
    event VaultImplAdded(address indexed impl);

    /**
     * @notice Event emitted on removing Vault implementation contract
     * @param impl The address of the removed implementation contract
     */
    event VaultImplRemoved(address indexed impl);

    /**
     * @notice Event emitted on whitelisting the factory
     * @param factory The address of the whitelisted factory
     */
    event FactoryAdded(address indexed factory);

    /**
     * @notice Event emitted on removing the factory from the whitelist
     * @param factory The address of the factory removed from the whitelist
     */
    event FactoryRemoved(address indexed factory);

    /**
     * @notice Registered Vaults
     * @param vault The address of the vault to check whether it is registered
     * @return `true` for the registered Vault, `false` otherwise
     */
    function vaults(address vault) external view returns (bool);

    /**
     * @notice Registered Vault implementations
     * @param impl The address of the vault implementation
     * @return `true` for the registered implementation, `false` otherwise
     */
    function vaultImpls(address impl) external view returns (bool);

    /**
     * @notice Registered Factories
     * @param factory The address of the factory to check whether it is whitelisted
     * @return `true` for the whitelisted Factory, `false` otherwise
     */
    function factories(address factory) external view returns (bool);

    /**
     * @notice Function for adding Vault to the registry. Can only be called by the whitelisted Factory.
     * @param vault The address of the Vault to add
     */
    function addVault(address vault) external;

    /**
     * @notice Function for adding Vault implementation contract
     * @param newImpl The address of the new implementation contract
     */
    function addVaultImpl(address newImpl) external;

    /**
     * @notice Function for removing Vault implementation contract
     * @param impl The address of the removed implementation contract
     */
    function removeVaultImpl(address impl) external;

    /**
     * @notice Function for adding the factory to the whitelist
     * @param factory The address of the factory to add to the whitelist
     */
    function addFactory(address factory) external;

    /**
     * @notice Function for removing the factory from the whitelist
     * @param factory The address of the factory to remove from the whitelist
     */
    function removeFactory(address factory) external;

    /**
     * @notice Function for initializing the registry. Can only be called once during the deployment.
     * @param _owner The address of the owner of the contract
     */
    function initialize(address _owner) external;
}
SafeCast.sol 1162 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool 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.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @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
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        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
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        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
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        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
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        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
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        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
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        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
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        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
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        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
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        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
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        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
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        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
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        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
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        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
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        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
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        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
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        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
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        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
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        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
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        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
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        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
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        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
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        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
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        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
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        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
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        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
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        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
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        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
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        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
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        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
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        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
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        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
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @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
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @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
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @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
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @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
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @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
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @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
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @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
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @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
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @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
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @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
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @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
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @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
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @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
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @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
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @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
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @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
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @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
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @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
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @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
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @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
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @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
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @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
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @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
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @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
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @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
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @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
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @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
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @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
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @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
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @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
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}
Math.sol 749 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Return the 512-bit addition of two uint256.
     *
     * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
     */
    function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        assembly ("memory-safe") {
            low := add(a, b)
            high := lt(low, a)
        }
    }

    /**
     * @dev Return the 512-bit multiplication of two uint256.
     *
     * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
     */
    function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
        // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = high * 2²⁵⁶ + low.
        assembly ("memory-safe") {
            let mm := mulmod(a, b, not(0))
            low := mul(a, b)
            high := sub(sub(mm, low), lt(mm, low))
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            success = c >= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a - b;
            success = c <= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a * b;
            assembly ("memory-safe") {
                // Only true when the multiplication doesn't overflow
                // (c / a == b) || (a == 0)
                success := or(eq(div(c, a), b), iszero(a))
            }
            // equivalent to: success ? c : 0
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `DIV` opcode returns zero when the denominator is 0.
                result := div(a, b)
            }
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `MOD` opcode returns zero when the denominator is 0.
                result := mod(a, b)
            }
        }
    }

    /**
     * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryAdd(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
     */
    function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
        (, uint256 result) = trySub(a, b);
        return result;
    }

    /**
     * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryMul(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);

            // Handle non-overflow cases, 256 by 256 division.
            if (high == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return low / denominator;
            }

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= high) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [high low].
            uint256 remainder;
            assembly ("memory-safe") {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                high := sub(high, gt(remainder, low))
                low := sub(low, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly ("memory-safe") {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [high low] by twos.
                low := div(low, twos)

                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from high into low.
            low |= high * twos;

            // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
            // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
            // is no longer required.
            result = low * inverse;
            return result;
        }
    }

    /**
     * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
     */
    function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);
            if (high >= 1 << n) {
                Panic.panic(Panic.UNDER_OVERFLOW);
            }
            return (high << (256 - n)) | (low >> n);
        }
    }

    /**
     * @dev Calculates x * y >> n with full precision, following the selected rounding direction.
     */
    function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
        return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

    /**
     * @dev Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // If upper 8 bits of 16-bit half set, add 8 to result
        r |= SafeCast.toUint((x >> r) > 0xff) << 3;
        // If upper 4 bits of 8-bit half set, add 4 to result
        r |= SafeCast.toUint((x >> r) > 0xf) << 2;

        // Shifts value right by the current result and use it as an index into this lookup table:
        //
        // | x (4 bits) |  index  | table[index] = MSB position |
        // |------------|---------|-----------------------------|
        // |    0000    |    0    |        table[0] = 0         |
        // |    0001    |    1    |        table[1] = 0         |
        // |    0010    |    2    |        table[2] = 1         |
        // |    0011    |    3    |        table[3] = 1         |
        // |    0100    |    4    |        table[4] = 2         |
        // |    0101    |    5    |        table[5] = 2         |
        // |    0110    |    6    |        table[6] = 2         |
        // |    0111    |    7    |        table[7] = 2         |
        // |    1000    |    8    |        table[8] = 3         |
        // |    1001    |    9    |        table[9] = 3         |
        // |    1010    |   10    |        table[10] = 3        |
        // |    1011    |   11    |        table[11] = 3        |
        // |    1100    |   12    |        table[12] = 3        |
        // |    1101    |   13    |        table[13] = 3        |
        // |    1110    |   14    |        table[14] = 3        |
        // |    1111    |   15    |        table[15] = 3        |
        //
        // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
        assembly ("memory-safe") {
            r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
        }
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
        return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
ExitQueue.sol 178 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Errors} from "./Errors.sol";

/**
 * @title ExitQueue
 * @author StakeWise
 * @notice ExitQueue represent checkpoints of burned shares and exited assets
 */
library ExitQueue {
    /**
     * @notice A struct containing checkpoint data
     * @param totalTickets The cumulative number of tickets (shares) exited
     * @param exitedAssets The number of assets that exited in this checkpoint
     */
    struct Checkpoint {
        uint160 totalTickets;
        uint96 exitedAssets;
    }

    /**
     * @notice A struct containing the history of checkpoints data
     * @param checkpoints An array of checkpoints
     */
    struct History {
        Checkpoint[] checkpoints;
    }

    /**
     * @notice Get the latest checkpoint total tickets
     * @param self An array containing checkpoints
     * @return The current total tickets or zero if there are no checkpoints
     */
    function getLatestTotalTickets(History storage self) internal view returns (uint256) {
        uint256 pos = self.checkpoints.length;
        unchecked {
            // cannot underflow as subtraction happens in case pos > 0
            return pos == 0 ? 0 : _unsafeAccess(self.checkpoints, pos - 1).totalTickets;
        }
    }

    /**
     * @notice Get checkpoint index for the burned shares
     * @param self An array containing checkpoints
     * @param positionTicket The position ticket to search the closest checkpoint for
     * @return The checkpoint index or the length of checkpoints array in case there is no such
     */
    function getCheckpointIndex(History storage self, uint256 positionTicket) external view returns (uint256) {
        uint256 high = self.checkpoints.length;
        uint256 low;
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self.checkpoints, mid).totalTickets > positionTicket) {
                high = mid;
            } else {
                unchecked {
                    // cannot underflow as mid < high
                    low = mid + 1;
                }
            }
        }
        return high;
    }

    /**
     * @notice Calculates burned shares and exited assets
     * @param self An array containing checkpoints
     * @param checkpointIdx The index of the checkpoint to start calculating from
     * @param positionTicket The position ticket to start calculating exited assets from
     * @param positionShares The number of shares to calculate assets for
     * @return burnedShares The number of shares burned
     * @return exitedAssets The number of assets exited
     */
    function calculateExitedAssets(
        History storage self,
        uint256 checkpointIdx,
        uint256 positionTicket,
        uint256 positionShares
    ) external view returns (uint256 burnedShares, uint256 exitedAssets) {
        uint256 length = self.checkpoints.length;
        // there are no exited assets for such checkpoint index or no shares to burn
        if (checkpointIdx >= length || positionShares == 0) return (0, 0);

        // previous total tickets for calculating how much shares were burned for the period
        uint256 prevTotalTickets;
        unchecked {
            // cannot underflow as subtraction happens in case checkpointIdx > 0
            prevTotalTickets = checkpointIdx == 0 ? 0 : _unsafeAccess(self.checkpoints, checkpointIdx - 1).totalTickets;
        }

        // current total tickets for calculating assets per burned share
        // can be used with _unsafeAccess as checkpointIdx < length
        Checkpoint memory checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx);
        uint256 currTotalTickets = checkpoint.totalTickets;
        uint256 checkpointAssets = checkpoint.exitedAssets;
        // check whether position ticket is in [prevTotalTickets, currTotalTickets) range
        if (positionTicket < prevTotalTickets || currTotalTickets <= positionTicket) {
            revert Errors.InvalidCheckpointIndex();
        }

        if (currTotalTickets > 1 && checkpointAssets == 0 && checkpointIdx + 1 < length) {
            // only single checkpoint created during V2 -> V3 migration can pass this if
            checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx + 1);
            uint256 totalShares = checkpoint.totalTickets - currTotalTickets;
            if (positionShares > totalShares) {
                revert Errors.InvalidShares();
            }
            return (positionShares, Math.mulDiv(positionShares, checkpoint.exitedAssets, totalShares));
        }

        // calculate amount of available shares that will be updated while iterating over checkpoints
        uint256 availableShares;
        unchecked {
            // cannot underflow as positionTicket < currTotalTickets
            availableShares = currTotalTickets - positionTicket;
        }

        // accumulate assets until the number of required shares is collected
        while (true) {
            unchecked {
                // cannot underflow as prevTotalTickets <= positionTicket
                uint256 checkpointShares = currTotalTickets - prevTotalTickets;
                // cannot underflow as positionShares > burnedShares while in the loop
                uint256 sharesDelta = Math.min(availableShares, positionShares - burnedShares);

                // cannot overflow as it is capped with underlying asset total supply
                burnedShares += sharesDelta;
                exitedAssets += Math.mulDiv(sharesDelta, checkpointAssets, checkpointShares);

                // cannot overflow as checkpoints are created max once per day
                checkpointIdx++;
            }

            // stop when required shares collected or reached end of checkpoints list
            if (positionShares <= burnedShares || checkpointIdx >= length) {
                return (burnedShares, exitedAssets);
            }

            // take next checkpoint
            prevTotalTickets = currTotalTickets;
            // can use _unsafeAccess as checkpointIdx < length is checked above
            checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx);
            currTotalTickets = checkpoint.totalTickets;
            checkpointAssets = checkpoint.exitedAssets;

            unchecked {
                // cannot underflow as every next checkpoint total tickets is larger than previous
                availableShares = currTotalTickets - prevTotalTickets;
            }
        }
    }

    /**
     * @notice Pushes a new checkpoint onto a History
     * @param self An array containing checkpoints
     * @param shares The number of shares to add to the latest checkpoint
     * @param assets The number of assets that were exited for this checkpoint
     */
    function push(History storage self, uint256 shares, uint256 assets) internal {
        if (shares == 0) revert Errors.InvalidCheckpointValue();
        Checkpoint memory checkpoint = Checkpoint({
            totalTickets: SafeCast.toUint160(getLatestTotalTickets(self) + shares),
            exitedAssets: SafeCast.toUint96(assets)
        });
        self.checkpoints.push(checkpoint);
    }

    function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private pure returns (Checkpoint storage result) {
        assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }
}
IOsTokenVaultController.sol 179 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title IOsTokenVaultController
 * @author StakeWise
 * @notice Defines the interface for the OsTokenVaultController contract
 */
interface IOsTokenVaultController {
    /**
     * @notice Event emitted on minting shares
     * @param vault The address of the Vault
     * @param receiver The address that received the shares
     * @param assets The number of assets collateralized
     * @param shares The number of tokens the owner received
     */
    event Mint(address indexed vault, address indexed receiver, uint256 assets, uint256 shares);

    /**
     * @notice Event emitted on burning shares
     * @param vault The address of the Vault
     * @param owner The address that owns the shares
     * @param assets The total number of assets withdrawn
     * @param shares The total number of shares burned
     */
    event Burn(address indexed vault, address indexed owner, uint256 assets, uint256 shares);

    /**
     * @notice Event emitted on state update
     * @param profitAccrued The profit accrued since the last update
     * @param treasuryShares The number of shares minted for the treasury
     * @param treasuryAssets The number of assets minted for the treasury
     */
    event StateUpdated(uint256 profitAccrued, uint256 treasuryShares, uint256 treasuryAssets);

    /**
     * @notice Event emitted on capacity update
     * @param capacity The amount after which the OsToken stops accepting deposits
     */
    event CapacityUpdated(uint256 capacity);

    /**
     * @notice Event emitted on treasury address update
     * @param treasury The new treasury address
     */
    event TreasuryUpdated(address indexed treasury);

    /**
     * @notice Event emitted on fee percent update
     * @param feePercent The new fee percent
     */
    event FeePercentUpdated(uint16 feePercent);

    /**
     * @notice Event emitted on average reward per second update
     * @param avgRewardPerSecond The new average reward per second
     */
    event AvgRewardPerSecondUpdated(uint256 avgRewardPerSecond);

    /**
     * @notice Event emitted on keeper address update
     * @param keeper The new keeper address
     */
    event KeeperUpdated(address keeper);

    /**
     * @notice The OsToken capacity
     * @return The amount after which the OsToken stops accepting deposits
     */
    function capacity() external view returns (uint256);

    /**
     * @notice The DAO treasury address that receives OsToken fees
     * @return The address of the treasury
     */
    function treasury() external view returns (address);

    /**
     * @notice The fee percent (multiplied by 100)
     * @return The fee percent applied by the OsToken on the rewards
     */
    function feePercent() external view returns (uint64);

    /**
     * @notice The address that can update avgRewardPerSecond
     * @return The address of the keeper contract
     */
    function keeper() external view returns (address);

    /**
     * @notice The average reward per second used to mint OsToken rewards
     * @return The average reward per second earned by the Vaults
     */
    function avgRewardPerSecond() external view returns (uint256);

    /**
     * @notice The fee per share used for calculating the fee for every position
     * @return The cumulative fee per share
     */
    function cumulativeFeePerShare() external view returns (uint256);

    /**
     * @notice The total number of shares controlled by the OsToken
     * @return The total number of shares
     */
    function totalShares() external view returns (uint256);

    /**
     * @notice Total assets controlled by the OsToken
     * @return The total amount of the underlying asset that is "managed" by OsToken
     */
    function totalAssets() external view returns (uint256);

    /**
     * @notice Converts assets to shares
     * @param assets The amount of assets to convert to shares
     * @return shares The amount of OsToken shares obtained when exchanging with the amount of assets provided
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @notice Converts shares to assets
     * @param shares The amount of shares to convert to assets
     * @return assets The amount of assets obtained when exchanging with the amount of OsToken shares provided
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @notice Updates rewards and treasury fee checkpoint for the OsToken
     */
    function updateState() external;

    /**
     * @notice Mint OsToken shares. Can only be called by the registered vault.
     * @param receiver The address that will receive the shares
     * @param shares The amount of shares to mint
     * @return assets The amount of assets minted
     */
    function mintShares(address receiver, uint256 shares) external returns (uint256 assets);

    /**
     * @notice Burn shares for withdrawn assets. Can only be called by the registered vault.
     * @param owner The address that owns the shares
     * @param shares The amount of shares to burn
     * @return assets The amount of assets withdrawn
     */
    function burnShares(address owner, uint256 shares) external returns (uint256 assets);

    /**
     * @notice Update treasury address. Can only be called by the owner.
     * @param _treasury The new treasury address
     */
    function setTreasury(address _treasury) external;

    /**
     * @notice Update capacity. Can only be called by the owner.
     * @param _capacity The amount after which the OsToken stops accepting deposits
     */
    function setCapacity(uint256 _capacity) external;

    /**
     * @notice Update fee percent. Can only be called by the owner. Cannot be larger than 10 000 (100%).
     * @param _feePercent The new fee percent
     */
    function setFeePercent(uint16 _feePercent) external;

    /**
     * @notice Update keeper address. Can only be called by the owner.
     * @param _keeper The new keeper address
     */
    function setKeeper(address _keeper) external;

    /**
     * @notice Updates average reward per second. Can only be called by the keeper.
     * @param _avgRewardPerSecond The new average reward per second
     */
    function setAvgRewardPerSecond(uint256 _avgRewardPerSecond) external;
}
IOsTokenConfig.sol 63 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title IOsTokenConfig
 * @author StakeWise
 * @notice Defines the interface for the OsTokenConfig contract
 */
interface IOsTokenConfig {
    /**
     * @notice Emitted when OsToken minting and liquidating configuration values are updated
     * @param vault The address of the vault to update the config for. Will be zero address if it is a default config.
     * @param liqBonusPercent The new liquidation bonus percent value
     * @param liqThresholdPercent The new liquidation threshold percent value
     * @param ltvPercent The new loan-to-value (LTV) percent value
     */
    event OsTokenConfigUpdated(address vault, uint128 liqBonusPercent, uint64 liqThresholdPercent, uint64 ltvPercent);

    /**
     * @notice Emitted when the OsToken redeemer address is updated
     * @param newRedeemer The address of the new redeemer
     */
    event RedeemerUpdated(address newRedeemer);

    /**
     * @notice The OsToken minting and liquidating configuration values
     * @param liqThresholdPercent The liquidation threshold percent used to calculate health factor for OsToken position
     * @param liqBonusPercent The minimal bonus percent that liquidator earns on OsToken position liquidation
     * @param ltvPercent The percent used to calculate how much user can mint OsToken shares
     */
    struct Config {
        uint128 liqBonusPercent;
        uint64 liqThresholdPercent;
        uint64 ltvPercent;
    }

    /**
     * @notice The address of the OsToken redeemer
     * @return The address of the redeemer
     */
    function redeemer() external view returns (address);

    /**
     * @notice Returns the OsToken minting and liquidating configuration values for the vault
     * @param vault The address of the vault to get the config for
     * @return config The OsToken config for the vault
     */
    function getConfig(address vault) external view returns (Config memory config);

    /**
     * @notice Sets the OsToken redeemer address. Can only be called by the owner.
     * @param newRedeemer The address of the new redeemer
     */
    function setRedeemer(address newRedeemer) external;

    /**
     * @notice Updates the OsToken minting and liquidating configuration values. Can only be called by the owner.
     * @param vault The address of the vault. Set to zero address to update the default config.
     * @param config The new OsToken configuration
     */
    function updateConfig(address vault, Config memory config) external;
}
IOsTokenVaultEscrow.sol 210 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {IMulticall} from "./IMulticall.sol";

/**
 * @title IOsTokenVaultEscrow
 * @author StakeWise
 * @notice Interface for OsTokenVaultEscrow contract
 */
interface IOsTokenVaultEscrow is IMulticall {
    /**
     * @notice Struct to store the escrow position details
     * @param owner The address of the assets owner
     * @param exitedAssets The amount of assets exited and ready to be claimed
     * @param osTokenShares The amount of osToken shares
     * @param cumulativeFeePerShare The cumulative fee per share used to calculate the osToken fee
     */
    struct Position {
        address owner;
        uint96 exitedAssets;
        uint128 osTokenShares;
        uint128 cumulativeFeePerShare;
    }

    /**
     * @notice Event emitted on position creation
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param owner The address of the assets owner
     * @param osTokenShares The amount of osToken shares
     * @param cumulativeFeePerShare The cumulative fee per share used to calculate the osToken fee
     */
    event PositionCreated(
        address indexed vault,
        uint256 indexed exitPositionTicket,
        address owner,
        uint256 osTokenShares,
        uint256 cumulativeFeePerShare
    );

    /**
     * @notice Event emitted on assets exit processing
     * @param vault The address of the vault
     * @param caller The address of the caller
     * @param exitPositionTicket The exit position ticket
     * @param exitedAssets The amount of exited assets claimed
     */
    event ExitedAssetsProcessed(
        address indexed vault, address indexed caller, uint256 indexed exitPositionTicket, uint256 exitedAssets
    );

    /**
     * @notice Event emitted on osToken liquidation
     * @param caller The address of the function caller
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param receiver The address of the receiver of the liquidated assets
     * @param osTokenShares The amount of osToken shares to liquidate
     * @param receivedAssets The amount of assets received
     */
    event OsTokenLiquidated(
        address indexed caller,
        address indexed vault,
        uint256 indexed exitPositionTicket,
        address receiver,
        uint256 osTokenShares,
        uint256 receivedAssets
    );

    /**
     * @notice Event emitted on osToken redemption
     * @param caller The address of the function caller
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param receiver The address of the receiver of the redeemed assets
     * @param osTokenShares The amount of osToken shares to redeem
     * @param receivedAssets The amount of assets received
     */
    event OsTokenRedeemed(
        address indexed caller,
        address indexed vault,
        uint256 indexed exitPositionTicket,
        address receiver,
        uint256 osTokenShares,
        uint256 receivedAssets
    );

    /**
     * @notice Event emitted on exited assets claim
     * @param receiver The address of the receiver of the exited assets
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param osTokenShares The amount of osToken shares burned
     * @param assets The amount of assets claimed
     */
    event ExitedAssetsClaimed(
        address indexed receiver,
        address indexed vault,
        uint256 indexed exitPositionTicket,
        uint256 osTokenShares,
        uint256 assets
    );

    /**
     * @notice Event emitted on liquidation configuration update
     * @param liqThresholdPercent The liquidation threshold percent
     * @param liqBonusPercent The liquidation bonus percent
     */
    event LiqConfigUpdated(uint64 liqThresholdPercent, uint256 liqBonusPercent);

    /**
     * @notice Event emitted on authenticator update
     * @param newAuthenticator The address of the new authenticator
     */
    event AuthenticatorUpdated(address newAuthenticator);

    /**
     * @notice The liquidation threshold percent
     * @return The liquidation threshold percent starting from which the osToken shares can be liquidated
     */
    function liqThresholdPercent() external view returns (uint64);

    /**
     * @notice The liquidation bonus percent
     * @return The liquidation bonus percent paid for liquidating the osToken shares
     */
    function liqBonusPercent() external view returns (uint256);

    /**
     * @notice The address of the authenticator
     * @return The address of the authenticator contract
     */
    function authenticator() external view returns (address);

    /**
     * @notice Get the position details
     * @param vault The address of the vault
     * @param positionTicket The exit position ticket
     * @return owner The address of the assets owner
     * @return exitedAssets The amount of assets exited and ready to be claimed
     * @return osTokenShares The amount of osToken shares
     */
    function getPosition(address vault, uint256 positionTicket) external view returns (address, uint256, uint256);

    /**
     * @notice Registers the new escrow position
     * @param owner The address of the exited assets owner
     * @param exitPositionTicket The exit position ticket
     * @param osTokenShares The amount of osToken shares
     * @param cumulativeFeePerShare The cumulative fee per share used to calculate the osToken fee
     */
    function register(address owner, uint256 exitPositionTicket, uint256 osTokenShares, uint256 cumulativeFeePerShare)
        external;

    /**
     * @notice Claims exited assets from the vault to the escrow
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param timestamp The timestamp of the exit
     * @param exitQueueIndex The index of the exit in the queue
     */
    function processExitedAssets(address vault, uint256 exitPositionTicket, uint256 timestamp, uint256 exitQueueIndex)
        external;

    /**
     * @notice Claims the exited assets from the escrow to the owner. Can only be called by the position owner.
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param osTokenShares The amount of osToken shares to burn
     * @return claimedAssets The amount of assets claimed
     */
    function claimExitedAssets(address vault, uint256 exitPositionTicket, uint256 osTokenShares)
        external
        returns (uint256 claimedAssets);

    /**
     * @notice Liquidates the osToken shares
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param osTokenShares The amount of osToken shares to liquidate
     * @param receiver The address of the receiver of the liquidated assets
     */
    function liquidateOsToken(address vault, uint256 exitPositionTicket, uint256 osTokenShares, address receiver)
        external;

    /**
     * @notice Redeems the osToken shares. Can only be called by the osToken redeemer.
     * @param vault The address of the vault
     * @param exitPositionTicket The exit position ticket
     * @param osTokenShares The amount of osToken shares to redeem
     * @param receiver The address of the receiver of the redeemed assets
     */
    function redeemOsToken(address vault, uint256 exitPositionTicket, uint256 osTokenShares, address receiver)
        external;

    /**
     * @notice Updates the authenticator. Can only be called by the owner.
     * @param newAuthenticator The address of the new authenticator
     */
    function setAuthenticator(address newAuthenticator) external;

    /**
     * @notice Updates the liquidation configuration. Can only be called by the owner.
     * @param _liqThresholdPercent The liquidation threshold percent
     * @param _liqBonusPercent The liquidation bonus percent
     */
    function updateLiqConfig(uint64 _liqThresholdPercent, uint256 _liqBonusPercent) external;
}
OsTokenUtils.sol 79 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IOsTokenConfig} from "../interfaces/IOsTokenConfig.sol";
import {IOsTokenVaultController} from "../interfaces/IOsTokenVaultController.sol";
import {Errors} from "./Errors.sol";

/**
 * @title OsTokenUtils
 * @author StakeWise
 * @notice Includes functionality for handling osToken redemptions
 */
library OsTokenUtils {
    uint256 private constant _wad = 1e18;
    uint256 private constant _hfLiqThreshold = 1e18;
    uint256 private constant _maxPercent = 1e18;
    uint256 private constant _disabledLiqThreshold = type(uint64).max;

    /**
     * @dev Struct for storing redemption data
     * @param mintedAssets The amount of minted assets
     * @param depositedAssets The amount of deposited assets
     * @param redeemedOsTokenShares The amount of redeemed osToken shares
     * @param availableAssets The amount of available assets
     * @param isLiquidation Whether the redemption is a liquidation
     */
    struct RedemptionData {
        uint256 mintedAssets;
        uint256 depositedAssets;
        uint256 redeemedOsTokenShares;
        uint256 availableAssets;
        bool isLiquidation;
    }

    /**
     * @dev Calculates the amount of received assets during osToken redemption
     * @param osTokenConfig The address of the osToken config contract
     * @param osTokenVaultController The address of the osToken vault controller contract
     * @param data The redemption data
     * @return receivedAssets The amount of received assets
     */
    function calculateReceivedAssets(
        IOsTokenConfig osTokenConfig,
        IOsTokenVaultController osTokenVaultController,
        RedemptionData memory data
    ) external view returns (uint256 receivedAssets) {
        // SLOAD to memory
        IOsTokenConfig.Config memory config = osTokenConfig.getConfig(address(this));

        // calculate received assets
        if (data.isLiquidation) {
            // liquidations are only allowed if the liquidation threshold is not disabled
            if (config.liqThresholdPercent == _disabledLiqThreshold) {
                revert Errors.LiquidationDisabled();
            }
            // check health factor violation in case of liquidation
            if (
                Math.mulDiv(data.depositedAssets * _wad, config.liqThresholdPercent, data.mintedAssets * _maxPercent)
                    >= _hfLiqThreshold
            ) {
                revert Errors.InvalidHealthFactor();
            }
            receivedAssets = Math.mulDiv(
                osTokenVaultController.convertToAssets(data.redeemedOsTokenShares), config.liqBonusPercent, _maxPercent
            );
        } else {
            receivedAssets = osTokenVaultController.convertToAssets(data.redeemedOsTokenShares);
        }

        // check whether received assets are valid
        if (receivedAssets > data.depositedAssets || receivedAssets > data.availableAssets) {
            revert Errors.InvalidReceivedAssets();
        }

        return receivedAssets;
    }
}
IEthValidatorsRegistry.sol 27 lines
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.22;

import {IValidatorsRegistry} from "./IValidatorsRegistry.sol";

/**
 * @title IEthValidatorsRegistry
 * @author Ethereum Foundation
 * @notice This is the Ethereum validators deposit contract interface.
 *         See https://github.com/ethereum/consensus-specs/blob/v1.2.0/solidity_deposit_contract/deposit_contract.sol.
 *         For more information see the Phase 0 specification under https://github.com/ethereum/consensus-specs.
 */
interface IEthValidatorsRegistry is IValidatorsRegistry {
    /// @notice Submit a Phase 0 DepositData object.
    /// @param pubkey A BLS12-381 public key.
    /// @param withdrawal_credentials Commitment to a public key for withdrawals.
    /// @param signature A BLS12-381 signature.
    /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
    /// Used as a protection against malformed input.
    function deposit(
        bytes calldata pubkey,
        bytes calldata withdrawal_credentials,
        bytes calldata signature,
        bytes32 deposit_data_root
    ) external payable;
}
ISharedMevEscrow.sol 32 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title ISharedMevEscrow
 * @author StakeWise
 * @notice Defines the interface for the SharedMevEscrow contract
 */
interface ISharedMevEscrow {
    /**
     * @notice Event emitted on received MEV
     * @param assets The amount of MEV assets received
     */
    event MevReceived(uint256 assets);

    /**
     * @notice Event emitted on harvest
     * @param caller The function caller
     * @param assets The amount of assets withdrawn
     */
    event Harvested(address indexed caller, uint256 assets);

    /**
     * @notice Withdraws MEV accumulated in the escrow. Can be called only by the Vault.
     * @dev IMPORTANT: because control is transferred to the Vault, care must be
     *    taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
     *    https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
     * @param assets The amount of assets to withdraw
     */
    function harvest(uint256 assets) external;
}
IOwnMevEscrow.sol 37 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.22;

/**
 * @title IOwnMevEscrow
 * @author StakeWise
 * @notice Defines the interface for the OwnMevEscrow contract
 */
interface IOwnMevEscrow {
    /**
     * @notice Event emitted on received MEV
     * @param assets The amount of MEV assets received
     */
    event MevReceived(uint256 assets);

    /**
     * @notice Event emitted on harvest
     * @param assets The amount of assets withdrawn
     */
    event Harvested(uint256 assets);

    /**
     * @notice Vault address
     * @return The address of the vault that owns the escrow
     */
    function vault() external view returns (address payable);

    /**
     * @notice Withdraws MEV accumulated in the escrow. Can be called only by the Vault.
     * @dev IMPORTANT: because control is transferred to the Vault, care must be
     *    taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
     *    https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
     * @return assets The amount of assets withdrawn
     */
    function harvest() external returns (uint256 assets);
}
draft-IERC1822.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.20;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}
Errors.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}
MessageHashUtils.sol 99 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

import {Strings} from "../Strings.sol";

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an ERC-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        assembly ("memory-safe") {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an ERC-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an ERC-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
     */
    function toDataWithIntendedValidatorHash(
        address validator,
        bytes32 messageHash
    ) internal pure returns (bytes32 digest) {
        assembly ("memory-safe") {
            mstore(0x00, hex"19_00")
            mstore(0x02, shl(96, validator))
            mstore(0x16, messageHash)
            digest := keccak256(0x00, 0x36)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}
SignatureChecker.sol 50 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.20;

import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC-1271 signatures from smart contract wallets like
 * Argent and Safe Wallet (previously Gnosis Safe).
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC-1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        if (signer.code.length == 0) {
            (address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
            return err == ECDSA.RecoverError.NoError && recovered == signer;
        } else {
            return isValidERC1271SignatureNow(signer, hash, signature);
        }
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC-1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}
IBeacon.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}
IERC1967.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
 */
interface IERC1967 {
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);
}
StorageSlot.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC-1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns a `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }
}
Panic.sol 57 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}
IValidatorsRegistry.sol 17 lines
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.22;

/**
 * @title IValidatorsRegistry
 * @author Ethereum Foundation
 * @notice The validators deposit contract common interface
 */
interface IValidatorsRegistry {
    /// @notice A processed deposit event.
    event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index);

    /// @notice Query the current deposit root hash.
    /// @return The deposit root hash.
    function get_deposit_root() external view returns (bytes32);
}
Strings.sol 490 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    using SafeCast for *;

    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;
    uint256 private constant SPECIAL_CHARS_LOOKUP =
        (1 << 0x08) | // backspace
            (1 << 0x09) | // tab
            (1 << 0x0a) | // newline
            (1 << 0x0c) | // form feed
            (1 << 0x0d) | // carriage return
            (1 << 0x22) | // double quote
            (1 << 0x5c); // backslash

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev The string being parsed contains characters that are not in scope of the given base.
     */
    error StringsInvalidChar();

    /**
     * @dev The string being parsed is not a properly formatted address.
     */
    error StringsInvalidAddressFormat();

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            assembly ("memory-safe") {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                assembly ("memory-safe") {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
     * representation, according to EIP-55.
     */
    function toChecksumHexString(address addr) internal pure returns (string memory) {
        bytes memory buffer = bytes(toHexString(addr));

        // hash the hex part of buffer (skip length + 2 bytes, length 40)
        uint256 hashValue;
        assembly ("memory-safe") {
            hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
        }

        for (uint256 i = 41; i > 1; --i) {
            // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
            if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
                // case shift by xoring with 0x20
                buffer[i] ^= 0x20;
            }
            hashValue >>= 4;
        }
        return string(buffer);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }

    /**
     * @dev Parse a decimal string and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input) internal pure returns (uint256) {
        return parseUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        uint256 result = 0;
        for (uint256 i = begin; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 9) return (false, 0);
            result *= 10;
            result += chr;
        }
        return (true, result);
    }

    /**
     * @dev Parse a decimal string and returns the value as a `int256`.
     *
     * Requirements:
     * - The string must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input) internal pure returns (int256) {
        return parseInt(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
        (bool success, int256 value) = tryParseInt(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
     * the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
        return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
    }

    uint256 private constant ABS_MIN_INT256 = 2 ** 255;

    /**
     * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character or if the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, int256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseIntUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseIntUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, int256 value) {
        bytes memory buffer = bytes(input);

        // Check presence of a negative sign.
        bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        bool positiveSign = sign == bytes1("+");
        bool negativeSign = sign == bytes1("-");
        uint256 offset = (positiveSign || negativeSign).toUint();

        (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);

        if (absSuccess && absValue < ABS_MIN_INT256) {
            return (true, negativeSign ? -int256(absValue) : int256(absValue));
        } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
            return (true, type(int256).min);
        } else return (false, 0);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input) internal pure returns (uint256) {
        return parseHexUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseHexUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
     * invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseHexUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseHexUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        // skip 0x prefix if present
        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 offset = hasPrefix.toUint() * 2;

        uint256 result = 0;
        for (uint256 i = begin + offset; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 15) return (false, 0);
            result *= 16;
            unchecked {
                // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
                // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
                result += chr;
            }
        }
        return (true, result);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input) internal pure returns (address) {
        return parseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
        (bool success, address value) = tryParseAddress(input, begin, end);
        if (!success) revert StringsInvalidAddressFormat();
        return value;
    }

    /**
     * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
     * formatted address. See {parseAddress-string} requirements.
     */
    function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
        return tryParseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
     * formatted address. See {parseAddress-string-uint256-uint256} requirements.
     */
    function tryParseAddress(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, address value) {
        if (end > bytes(input).length || begin > end) return (false, address(0));

        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 expectedLength = 40 + hasPrefix.toUint() * 2;

        // check that input is the correct length
        if (end - begin == expectedLength) {
            // length guarantees that this does not overflow, and value is at most type(uint160).max
            (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
            return (s, address(uint160(v)));
        } else {
            return (false, address(0));
        }
    }

    function _tryParseChr(bytes1 chr) private pure returns (uint8) {
        uint8 value = uint8(chr);

        // Try to parse `chr`:
        // - Case 1: [0-9]
        // - Case 2: [a-f]
        // - Case 3: [A-F]
        // - otherwise not supported
        unchecked {
            if (value > 47 && value < 58) value -= 48;
            else if (value > 96 && value < 103) value -= 87;
            else if (value > 64 && value < 71) value -= 55;
            else return type(uint8).max;
        }

        return value;
    }

    /**
     * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
     *
     * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
     *
     * NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
     * RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
     * characters that are not in this range, but other tooling may provide different results.
     */
    function escapeJSON(string memory input) internal pure returns (string memory) {
        bytes memory buffer = bytes(input);
        bytes memory output = new bytes(2 * buffer.length); // worst case scenario
        uint256 outputLength = 0;

        for (uint256 i; i < buffer.length; ++i) {
            bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
            if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
                output[outputLength++] = "\\";
                if (char == 0x08) output[outputLength++] = "b";
                else if (char == 0x09) output[outputLength++] = "t";
                else if (char == 0x0a) output[outputLength++] = "n";
                else if (char == 0x0c) output[outputLength++] = "f";
                else if (char == 0x0d) output[outputLength++] = "r";
                else if (char == 0x5c) output[outputLength++] = "\\";
                else if (char == 0x22) {
                    // solhint-disable-next-line quotes
                    output[outputLength++] = '"';
                }
            } else {
                output[outputLength++] = char;
            }
        }
        // write the actual length and deallocate unused memory
        assembly ("memory-safe") {
            mstore(output, outputLength)
            mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
        }

        return string(output);
    }

    /**
     * @dev Reads a bytes32 from a bytes array without bounds checking.
     *
     * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
     * assembly block as such would prevent some optimizations.
     */
    function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
        // This is not memory safe in the general case, but all calls to this private function are within bounds.
        assembly ("memory-safe") {
            value := mload(add(buffer, add(0x20, offset)))
        }
    }
}
ECDSA.sol 180 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(
        bytes32 hash,
        bytes memory signature
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly ("memory-safe") {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}
IERC1271.sol 17 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with `hash`
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
SignedMath.sol 68 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
        }
    }

    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
            // taking advantage of the most significant (or "sign" bit) in two's complement representation.
            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
            // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
            int256 mask = n >> 255;

            // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
            return uint256((n + mask) ^ mask);
        }
    }
}

Read Contract

UPGRADE_INTERFACE_VERSION 0xad3cb1cc → string
admin 0xf851a440 → address
calculateExitedAssets 0x76b58b90 → uint256, uint256, uint256
capacity 0x5cfc1a51 → uint256
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
feePercent 0x7fd6f15c → uint16
feeRecipient 0x46904840 → address
getExitQueueData 0x3e1655d3 → uint128, uint128, uint128, uint128, uint256
getExitQueueIndex 0x60d60e6e → int256
getShares 0xf04da65b → uint256
implementation 0x5c60da1b → address
isStateUpdateRequired 0x72b410a8 → bool
mevEscrow 0x3229fa95 → address
osTokenPositions 0x4ec96b22 → uint128
proxiableUUID 0x52d1902d → bytes32
totalAssets 0x01e1d114 → uint256
totalShares 0x3a98ef39 → uint256
v2Validators 0x776ae2b0 → bool
validatorsManager 0xb1f0e7c7 → address
validatorsManagerNonce 0x24a14b14 → uint256
vaultId 0x33194c0a → bytes32
version 0x54fd4d50 → uint8
withdrawableAssets 0x2cdf7401 → uint256

Write Contract 27 functions

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

burnOsToken 0x066055e0
uint128 osTokenShares
returns: uint256
claimExitedAssets 0x8697d2c2
uint256 positionTicket
uint256 timestamp
uint256 exitQueueIndex
consolidateValidators 0x5c0935db
bytes validators
bytes validatorsManagerSignature
bytes oracleSignatures
deposit 0xf9609f08
address receiver
address referrer
returns: uint256
depositAndMintOsToken 0x36fe59d2
address receiver
uint256 osTokenShares
address referrer
returns: uint256
donateAssets 0x1913bb24
No parameters
enterExitQueue 0x8ceab9aa
uint256 shares
address receiver
returns: uint256
fundValidators 0xa145ca01
bytes validators
bytes validatorsManagerSignature
initialize 0x439fab91
bytes params
liquidateOsToken 0x2999ad3f
uint256 osTokenShares
address owner
address receiver
mintOsToken 0x201b9eb5
address receiver
uint256 osTokenShares
address referrer
returns: uint256
multicall 0xac9650d8
bytes[] data
returns: bytes[]
receiveFromMevEscrow 0x53156f28
No parameters
redeemOsToken 0x43e82a79
uint256 osTokenShares
address owner
address receiver
registerValidators 0x09fdcd3a
tuple keeperParams
bytes validatorsManagerSignature
rescueAssets 0x78f61ca8
No parameters
setAdmin 0x704b6c02
address newAdmin
setFeePercent 0x0402f196
uint16 _feePercent
setFeeRecipient 0xe74b981b
address _feeRecipient
setMetadata 0xa49a1e7d
string metadataIpfsHash
setValidatorsManager 0x754c3888
address _validatorsManager
transferOsTokenPositionToEscrow 0x9267842a
uint256 osTokenShares
returns: uint256
updateState 0x6971af8f
tuple harvestParams
updateStateAndDeposit 0x479476d8
address receiver
address referrer
tuple harvestParams
returns: uint256
updateStateAndDepositAndMintOsToken 0x8d4c30c0
address receiver
uint256 osTokenShares
address referrer
tuple harvestParams
returns: uint256
upgradeToAndCall 0x4f1ef286
address newImplementation
bytes data
withdrawValidators 0xd40a902f
bytes validators
bytes validatorsManagerSignature

Recent Transactions

No transactions found for this address