Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xB53a6c402b0d4fb6c7AA59B7d8FBD2e884FbF3bC
Balance 0 ETH
Nonce 1
Code Size 19121 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

19121 bytes
0x60806040526004361015610022575b3615610018575f80fd5b610020612f18565b005b5f3560e01c806301e1d11414610321578063066055e01461031c57806307a2d13a146103175780630d392cd91461031257806318f729501461030d5780631a7ff55314610308578063201b9eb51461030357806322758a4a146102fe5780632999ad3f146102f95780632cdf7401146102f45780633229fa95146102ef57806333194c0a146102ea5780633a98ef39146102e5578063439fab91146102e057806343e82a79146102db57806346904840146102d65780634ec96b22146102d15780634f1ef286146102cc578063514e2708146102c757806352d1902d146102c257806353156f28146102bd57806354fd4d50146102b85780635c60da1b146102b35780635cfc1a51146102ae5780635dddf3a8146102a957806360d60e6e146102a457806372b410a81461029f57806376b58b901461029a5780637bde82f2146102955780637fd6f15c146102905780638697d2c21461028b5780638ceab9aa146102865780639b401cde14610281578063a1bf49aa1461027c578063a49a1e7d14610277578063aaa4e83614610272578063ac9650d81461026d578063ad3cb1cc14610268578063c6e6f59214610263578063d83ad00c1461025e578063e74b981b14610259578063ef2a215814610254578063f04da65b1461024f578063f5e9de4d1461024a578063f6a6830f14610245578063f851a44014610240578063f9609f081461023b5763f98f5b920361000e576124a1565b612477565b612450565b61240f565b61227f565b612244565b6121bc565b612181565b61215b565b61213d565b6120f8565b612003565b611ebd565b611e66565b611e49565b611e2c565b611ce6565b611cc1565b611c9d565b611b91565b611b40565b611ab3565b611a0e565b6119f4565b6119da565b6119a6565b61198b565b611967565b6118fe565b611892565b61160c565b6114b3565b61148b565b61104c565b610f33565b610ec4565b610e8a565b610e5e565b610e44565b610a29565b6109d4565b6106a7565b610674565b61061d565b610540565b6104ff565b610354565b610334565b5f91031261033057565b5f80fd5b34610330575f36600319011261033057602060985460801c604051908152f35b34610330576020366003190112610330576001600160801b036004358181169081810361033057604051633b9e9f0160e21b81523360048201526001600160801b0382166024820152916020836044815f6001600160a01b037f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306165af19283156104fa575f936104c9575b50335f908152610137602052604090206103f8906124e8565b9361040a85516001600160801b031690565b16156104b7578361044c61043f61042f6104659461042a6104b399612f38565b612fe8565b83516001600160801b0316612521565b6001600160801b03168252565b335f9081526101376020526040902061253a565b61253a565b604080518381526001600160801b0392909216602083015233917f3f7354ba02880b4fa37a629985852a38417ff369369ce1e52fa6f8342a9100a79190a26040519081529081906020820190565b0390f35b60405163673f032f60e11b8152600490fd5b6104ec91935060203d6020116104f3575b6104e4818361158b565b8101906124ce565b915f6103df565b503d6104da565b6124dd565b3461033057602036600319011261033057602061051d60043561256c565b604051908152f35b6001600160a01b0381160361033057565b8015150361033057565b34610330576040806003193601126103305760043561055e81610525565b6024359061056b82610536565b610201546001600160a01b039190821633036105fe571691825f52610202918260205260ff825f2054169281151580941515146105ed57906105c191855f52602052825f209060ff801983541691151516179055565b519081527fd9c6c3eabe38e3b9a606a66358d8f225489216a59eeba66facefb7d91663526660203392a3005b8251637d5bf36f60e11b8152600490fd5b8351634ca8886760e01b8152600490fd5b908160809103126103305790565b60603660031901126103305760043561063581610525565b60243561064181610525565b604435906001600160401b0382116103305760209261066f61066a61051d94369060040161060f565b612592565b612e01565b34610330576020366003190112610330576004356001600160401b0381116103305761066a61002091369060040161060f565b346103305760603660031901126103305760048035906106c682610525565b60243590604435926106d784610525565b6106df6135b2565b6106e761363e565b604080516329460cc560e11b81526001600160a01b038381168286019081526020818101889052929693959192907f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003068416908290899081908a0103815f855af19788156104fa575f986109b5575b50335f908152610137602052604090208390610770906124e8565b946001600160801b0361078a87516001600160801b031690565b16156109535761079986612f38565b6107c56107b86107a889612fe8565b88516001600160801b03166125c3565b6001600160801b03168752565b335f908152609c6020526040902084906107e0905b5461256c565b918a5193848092631331885160e31b82527f000000000000000000000000e8822246f8864da92015813a39ae776087fb1cd5165afa9182156104fa5761086f928592610833925f92610934575b5061303e565b9261084587516001600160801b031690565b908a5180809581946303d1689d60e11b83528a83019190916001600160801b036020820193169052565b03915afa9283156104fa575f93610915575b5050106109075750947fa16d97739893e1436c9753925fb5cef174c4f368699dc86cc8fdb0e6e60f8e58916108ce6104b3976104603360018060a01b03165f5261013760205260405f2090565b84516001600160a01b039485168152602081018790526040810191909152921660608301523391608090a2519081529081906020820190565b8451633684c65960e01b8152fd5b61092c929350803d106104f3576104e4818361158b565b905f80610881565b61094c919250843d86116104f3576104e4818361158b565b905f61082d565b885163752a536d60e01b8152915083828681865afa80156104fa576109846109939187945f91610998575b50612fe8565b6001600160801b031687860152565b610799565b6109af9150873d89116104f3576104e4818361158b565b5f61097e565b6109cd919850823d84116104f3576104e4818361158b565b965f610755565b34610330575f36600319011261033057610201546040516001600160a01b039091168152602090f35b60609060031901126103305760043590602435610a1981610525565b90604435610a2681610525565b90565b3461033057610a37366109fd565b906001600160a01b0380831615610e3257610a5061363e565b807f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003061691823b156103305760408051631d8557d760e01b815260049491905f81878183875af180156104fa57610e19575b506001600160a01b0383165f90815261013760205260409020610ac3906124e8565b6001600160801b03610adc82516001600160801b031690565b1615610e0957610aeb81612f38565b81516330fe427560e21b81529260a08488817f000000000000000000000000e8822246f8864da92015813a39ae776087fb1cd58a165afa9687156104fa57610b5a975f955f91610dd0575b5084516303d1689d60e11b918282528c828060209d8e938883019190602083019252565b0381885afa80156104fa57610b75925f91610db3575061303e565b95610b936107da8960018060a01b03165f52609c60205260405f2090565b918288118015610da3575b610d9357908a610bdb92610bb988516001600160801b031690565b908951948592839283528883019190916001600160801b036020820193169052565b0381885afa9081156104fa57670de0b6b3a764000093610c17938d5f94610d6a575b5050610c0b610c1191612c67565b92612c84565b916130b8565b1015610d5c578351633b9e9f0160e21b815233918101918252602082018b905294939291889186919082905f90829060400103925af19081156104fa577f61fd285f9e34a3dbfa9846bdcf22a023e37a3c93549902843b30dd74a18c535097610d3995610cb193610d3e575b5050610c9461043f61042f8c612fe8565b6001600160a01b0386165f9081526101376020526040902061253a565b610cba82613b21565b90610cf8610cdd610cca85612fe8565b60985460801c036001600160801b031690565b6001600160801b036098549181199060801b16911617609855565b610d02828661428e565b610d0c8389613a74565b51948594169733978590949392606092608083019660018060a01b03168352602083015260408201520152565b0390a3005b81610d5492903d106104f3576104e4818361158b565b505f80610c83565b835163185cfc6d60e11b8152fd5b610c11929450610c0b9181610d8a92903d106104f3576104e4818361158b565b9391508d610bfd565b865163efda1a2760e01b81528490fd5b50610dac6125de565b8811610b9e565b610dca91508c8d3d106104f3576104e4818361158b565b5f61082d565b9050610df591955060a03d60a011610e02575b610ded818361158b565b8101906136c9565b509692509050945f610b36565b503d610de3565b815163673f032f60e11b81528690fd5b80610e26610e2c9261155d565b80610326565b5f610aa1565b60405163d92e233d60e01b8152600490fd5b34610330575f36600319011261033057602061051d6125de565b34610330575f366003190112610330576020610e7861260d565b6040516001600160a01b039091168152f35b34610330575f3660031901126103305760206040517fa90b5863127e5f962890f832f07b9f40c8df2fc043326a0e4b538552d600f2d98152f35b34610330575f3660031901126103305760206001600160801b0360985416604051908152f35b906020600319830112610330576004356001600160401b039283821161033057806023830112156103305781600401359384116103305760248483010111610330576024019190565b610f3c36610eea565b905f80516020614a5c83398151915254916001600160401b0360ff8460401c1615931680159081611044575b600114908161103a575b159081611031575b5061101f575f80516020614a5c833981519152805467ffffffffffffffff19166001179055610fad9183610ffb576126dc565b610fb357005b5f80516020614a5c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b5f80516020614a5c833981519152805460ff60401b1916600160401b1790556126dc565b60405163f92ee8a960e01b8152600490fd5b9050155f610f7a565b303b159150610f72565b849150610f68565b346103305761105a366109fd565b906001600160a01b039081831615610e325761107461363e565b817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f500030616803b156103305760408051631d8557d760e01b815260049291905f81858183875af180156104fa57611478575b506001600160a01b0384165f908152610137602052604090206110e6906124e8565b926001600160801b0361110085516001600160801b031690565b161561146a5761110f84612f38565b81516330fe427560e21b81529360a08583817f000000000000000000000000e8822246f8864da92015813a39ae776087fb1cd58b165afa9384156104fa575f955f9561143c575b5083516303d1689d60e11b8082528482018c81529197602094939285908a90819083015b0381865afa9889156104fa575f9961141d575b506001600160a01b038a165f908152609c602052604090206111ae906107da565b90818a11801561140d575b6113fd576111f490866111d387516001600160801b031690565b8a51809481928883528c83019190916001600160801b036020820193169052565b0381885afa9182156104fa575f926113dc575b50611212919261303e565b116113cc578551633b9e9f0160e21b815233868201908152602081018e905290919085908390819060400103815f875af19182156104fa5785926113af575b5061125b8d612fe8565b84516001600160801b03169061127091612521565b6001600160801b031684526001600160a01b038a165f908152610137602052604090208461129d9161253a565b6112a689613b21565b976112b08a612fe8565b60985460801c036001600160801b03166112df906001600160801b036098549181199060801b16911617609855565b6112e9898c61428e565b6001600160a01b038b165f908152609c602052604090205461130a9061256c565b906113149161303e565b935187519182526001600160801b0316868201908152909283918290036020019082905afa9283156104fa575f93611390575b505011611382575091610d39917f57f5eb636bf62215c111b54545422f11dfb0cb115f606be905f0be08e8859dd3959493610d0c8389613a74565b9051631d8fa13d60e31b8152fd5b6113a7929350803d106104f3576104e4818361158b565b905f80611347565b6113c590833d85116104f3576104e4818361158b565b505f611251565b855163324b20e160e11b81528590fd5b61121292506113f790883d8a116104f3576104e4818361158b565b91611207565b875163efda1a2760e01b81528790fd5b506114166125de565b8a116111b9565b611435919950853d87116104f3576104e4818361158b565b975f61118d565b61117a92965061145c91955060a03d60a011610e0257610ded818361158b565b505050959095949091611156565b905163673f032f60e11b8152fd5b80610e266114859261155d565b5f6110c4565b34610330575f366003190112610330576065546040516001600160a01b039091168152602090f35b34610330576020366003190112610330576004356114d081610525565b60018060a01b03165f52610137602052602060405f20604051906114f38261153d565b54906001600160801b03918281169081835260801c8483015261151b575b5116604051908152f35b61152481612f38565b611511565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b0382111761155857604052565b611529565b6001600160401b03811161155857604052565b606081019081106001600160401b0382111761155857604052565b90601f801991011681019081106001600160401b0382111761155857604052565b604051906115b98261153d565b565b6001600160401b03811161155857601f01601f191660200190565b9291926115e2826115bb565b916115f0604051938461158b565b829481845281830111610330578281602093845f960137010152565b60408060031936011261033057600490813561162781610525565b6024356001600160401b03811161033057366023820112156103305761165690369060248187013591016115d6565b9161165f61385b565b8051926116968461168860209363439fab9160e01b858401528460248401526044830190611f7b565b03601f19810186528561158b565b61169e61385b565b6116a6613b0e565b6001600160a01b0383811680159291908790841561185d575b84156117ef575b841561178b575b505082156116f5575b50506116e6576100208383614405565b516355299b4960e01b81528390fd5b83516345da87c560e01b81526001600160a01b03861688820190815292935091839183918290819060200103917f0000000000000000000000003a0008a588772446f6e656133c2d5029cc4fc20e165afa9182156104fa575f9261175e575b5050155f806116d6565b61177d9250803d10611784575b611775818361158b565b8101906127cb565b5f80611754565b503d61176b565b855163054fd4d560e41b81529294508391839182905afa9081156104fa5760029160ff915f916117c2575b5016141591865f6116cd565b6117e29150843d86116117e8575b6117da818361158b565b8101906143ec565b5f6117b6565b503d6117d0565b935050835163198ca60560e11b815282818981875afa9081156104fa5788917fa90b5863127e5f962890f832f07b9f40c8df2fc043326a0e4b538552d600f2d9915f91611840575b501415936116c6565b6118579150853d87116104f3576104e4818361158b565b5f611837565b5f80516020614a3c83398151915254909450849061188b906001600160a01b03165b6001600160a01b031690565b14936116bf565b34610330576020366003190112610330576004356001600160a01b036118b66127ac565b1633036118ec578060d0555f60d155337fbe9758e2d6800505eeb0292a08fe647c52762ccea131177456e7062dd77b41065f80a3005b604051634ca8886760e01b8152600490fd5b34610330575f366003190112610330577f000000000000000000000000b53a6c402b0d4fb6c7aa59b7d8fbd2e884fbf3bc6001600160a01b031630036119555760206040515f80516020614a3c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b5f366003190112610330576001600160a01b0361198261260d565b1633036118ec57005b34610330575f36600319011261033057602060405160018152f35b34610330575f366003190112610330575f80516020614a3c833981519152546040516001600160a01b039091168152602090f35b34610330575f36600319011261033057602061051d61279e565b34610330575f366003190112610330576020610e786127ac565b3461033057602036600319011261033057609a80549081905f6004355b848210611a5c57505050811015611a51576104b3905b6040519081529081906020820190565b506104b35f19611a41565b909193808316906001818518811c8301809311611aae575f8790525f80516020614a1c8339815191528301546001600160a01b0316841015611aa3575050935b9190611a2b565b909591019250611a9c565b61250d565b34610330575f36600319011261033057604051633eb1acf760e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa80156104fa576020915f91611b23575b506040519015158152f35b611b3a9150823d841161178457611775818361158b565b5f611b18565b34610330576080366003190112610330576104b3611b74600435611b6381610525565b6064359060443590602435906127ed565b604080519384526020840192909252908201529081906060820190565b34610330576040806003193601126103305760043560243591611bb383610525565b611bbb614906565b8115611c8d576001600160a01b0383168015611c7c57611bda8361256c565b92611be36125de565b8411611c6b57611c1a846104b396611c0b610cdd611c0084612fe8565b60985460801c612521565b611c15843361428e565b613a74565b8251848152602081019190915233907f5cdf07ad0fc222442720b108e3ed4c4640f0fadc2ab2253e66f259a0fea834809080604081015b0390a3611c5d336144b0565b519081529081906020820190565b82516396d8043360e01b8152600490fd5b815163d92e233d60e01b8152600490fd5b51636edcc52360e01b8152600490fd5b34610330575f36600319011261033057602061ffff60655460a01c16604051908152f35b34610330576060366003190112610330576104b3611b74604435602435600435612867565b3461033057604080600319360112610330576004359060243590611d0982610525565b611d116135b2565b8215611c8d576001600160a01b0382168015611c7c5783611df1611dd56104b396611d53611d476099546001600160801b031690565b6001600160801b031690565b81611db3611d6883611d636147ef565b61285a565b89516001600160a01b03909b1660208c019081524260408d015260608c01829052909a611da281608081015b03601f19810183528261158b565b5190205f52609b60205260405f2090565b55335f908152609c60205260409020611dcd8382546127e0565b905501612fe8565b6001600160801b03166001600160801b03196099541617609955565b8251848152602081019190915233907f211091c5bf013c1230f996c3bb2bc327e3de429a3d3c356dcea9a0c858bc407f908060408101611c51565b34610330575f36600319011261033057602060d054604051908152f35b34610330575f36600319011261033057602060d154604051908152f35b3461033057611eb87f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf611e9836610eea565b9290611ea2613b0e565b60405191829160208352339560208401916129fe565b0390a2005b3461033057602036600319011261033057600435611eda81610525565b611ee2613b0e565b6001600160a01b03168015610e325760d280546001600160a01b03191682179055337fb710e1d0ebf395f895942ac4e559a4a86d57f748a90cf05e9d70798c67e9c65b5f80a3005b9181601f84011215610330578235916001600160401b038311610330576020808501948460051b01011161033057565b5f5b838110611f6b5750505f910152565b8181015183820152602001611f5c565b90602091611f9481518092818552858086019101611f5a565b601f01601f1916010190565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310611fd55750505050505090565b9091929394958480611ff3600193603f198682030187528a51611f7b565b9801930193019194939290611fc5565b3461033057602036600319011261033057600480356001600160401b03811161033057612034903690600401611f2a565b9161203e83612a35565b925f5b81811061205657604051806104b38782611fa0565b5f80612063838588612ac4565b60409391612075855180938193612ae4565b0390305af490612083612af1565b91156120aa5750906001916120988288612b8b565b526120a38187612b8b565b5001612041565b84826044815110610330576120ce81836120e3930151602480918301019101612b20565b925162461bcd60e51b815292839283016120e7565b0390fd5b906020610a26928181520190611f7b565b34610330575f366003190112610330576104b36040516121178161153d565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611f7b565b3461033057602036600319011261033057602061051d600435613b21565b34610330575f3660031901126103305760206001600160801b0360995416604051908152f35b34610330576020366003190112610330576100206004356121a181610525565b6121a9613b0e565b613bb0565b908160a09103126103305790565b34610330576080366003190112610330576001600160401b03600435818111610330576121ed9036906004016121ae565b60243582811161033057612205903690600401611f2a565b60449291923584811161033057612220903690600401611f2a565b916064359586116103305761223c610020963690600401611f2a565b959094612c9b565b346103305760203660031901126103305760043561226181610525565b60018060a01b03165f52609c602052602060405f2054604051908152f35b3461033057604080600319360112610330576004906001600160401b038235818111610330576122b290369085016121ae565b90602435908111610330576122ca9036908501611f2a565b9390926122d561363e565b7f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b0316803b15610330575f8251809263837d444160e01b82528183816123248a8a8301612bd0565b03925af180156104fa576123fc575b506801bc16d674ec8000006123466125de565b106123ef578083019360b061235b8686612a92565b9050036123e1576123ad6123b19160d1549760d054906123a06123988b6123828c8c612a92565b611d948b94929451938492602084019687612dcd565b519020612dea565b6020815191012092613ea5565b1590565b6123d4576100206001866123ce6123c88888612a92565b90613edd565b0160d155565b516309bde33960e01b8152fd5b5051631a0a9b9f60e21b8152fd5b516396d8043360e01b8152fd5b80610e266124099261155d565b5f612333565b346103305760203660031901126103305760043561242c81610525565b60018060a01b03165f52610202602052602060ff60405f2054166040519015158152f35b34610330575f366003190112610330575f546040516001600160a01b039091168152602090f35b604036600319011261033057602061051d60043561249481610525565b6024359061066f82610525565b34610330576020366003190112610330576100206004356124c181610525565b6124c9613b0e565b613ff0565b90816020910312610330575190565b6040513d5f823e3d90fd5b906040516124f58161153d565b91546001600160801b038116835260801c6020830152565b634e487b7160e01b5f52601160045260245ffd5b6001600160801b039182169082160391908211611aae57565b815160209092015160801b6fffffffffffffffffffffffffffffffff19166001600160801b0392909216919091179055565b6098546001600160801b038116908161258457505090565b91610a269260801c906130b8565b61259b90613220565b90806125b4575b506125a957565b6125b16134b5565b50565b6125bd906133b6565b5f6125a2565b9190916001600160801b0380809416911601918211611aae57565b476099546125f46001600160801b03821661256c565b9060801c01908181115f14612607570390565b50505f90565b61016a546001600160a01b031680156126235790565b507f00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff8690565b908160209103126103305751610a2681610525565b906020828203126103305781356001600160401b0392838211610330570191606083830312610330576040519261269384611570565b80358452602081013561ffff81168103610330576020850152604081013591821161033057019080601f83011215610330578160206126d4933591016115d6565b604082015290565b60405163e7f6f22560e01b815290916020908183600481335afa9283156104fa575f9361277f575b50604051636f4fa30f60e01b8152938285600481335afa9081156104fa576115b995612747945f9361274c575b5050612740919281019061265d565b90836136f0565b6137cd565b6127409350908161277192903d10612778575b612769818361158b565b810190612648565b915f612731565b503d61275f565b612797919350823d841161277857612769818361158b565b915f612704565b609d5480610a2657505f1990565b60d2546001600160a01b03908116806127c657505f541690565b905090565b908160209103126103305751610a2681610536565b91908203918211611aae57565b604080516001600160a01b03909216602083019081529082019390935260608101829052909261283d9290916128268160808101611d94565b5190205f52609b60205260405f20549283916138d9565b9091828103908111611aae5792565b9060018201809211611aae57565b91908201809211611aae57565b929190915f936128977f00000000000000000000000000000000000000000000000000000000000151808561285a565b42106129d45760408051336020820190815291810186905260608082018490528152601f19916128d8916128cc60808261158b565b519020948684336127ed565b90969095878781156129c65750505f908152609b60205260408120556001821161297c575b50505061293561291a61290f85612fe8565b60995460801c612521565b6001600160801b036099549181199060801b16911617609955565b61293f8333613a74565b6040805191825260208201869052810183905233907feb3b05c070c24f667611fdb3ff75fe007d42401c573aed8d8faca95fd00ccb5690606090a2565b6129bd9192975061298d878561285a565b60408051336020820190815291810193909352606083018290526080998a0183529098909190611da2908261158b565b555f80806128fd565b959950975093955050505050565b604051631613b7eb60e01b8152600490fd5b90603060609281835260208301375f60508201520190565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b0381116115585760051b60200190565b90612a3f82612a1e565b612a4c604051918261158b565b8281528092612a5d601f1991612a1e565b01905f5b828110612a6d57505050565b806060602080938501015201612a61565b634e487b7160e01b5f52603260045260245ffd5b903590601e198136030182121561033057018035906001600160401b0382116103305760200191813603831361033057565b90821015612adf57612adb9160051b810190612a92565b9091565b612a7e565b908092918237015f815290565b3d15612b1b573d90612b02826115bb565b91612b10604051938461158b565b82523d5f602084013e565b606090565b602081830312610330578051906001600160401b038211610330570181601f82011215610330578051612b52816115bb565b92612b60604051948561158b565b8184526020828401011161033057610a269160208085019101611f5a565b805115612adf5760200190565b8051821015612adf5760209160051b010190565b9035601e19823603018112156103305701602081359101916001600160401b03821161033057813603831361033057565b9060a0610a2692602081528235602082015260208301356040820152612c0c612bfc6040850185612b9f565b84606085015260c08401916129fe565b90612c3f612c34612c206060870187612b9f565b601f198587038101608087015295916129fe565b946080810190612b9f565b939092828603019101526129fe565b906801bc16d674ec8000009180830292830403611aae57565b90670de0b6b3a764000091828102928184041490151715611aae57565b9061271091828102928184041490151715611aae57565b929094939195612ca961363e565b7f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b031695863b15610330576040965f8851809263837d444160e01b8252818381612cfd8c60048301612bd0565b03925af180156104fa57612dba575b50612d156125de565b612d1e89612c4e565b11612da95787158015612d8f575b612d7e5791612d5c959391612d56896123ad9795612d5060d054978c810190612a92565b90613d20565b94613e93565b612d6e57506115b99060d1540160d155565b516309bde33960e01b8152600490fd5b8651631c6c4cf360e31b8152600490fd5b50612d9c87860186612a92565b905060b089021415612d2c565b86516396d8043360e01b8152600490fd5b80610e26612dc79261155d565b5f612d0c565b939291602091612de5916040875260408701916129fe565b930152565b90604051916020830152602082526115b98261153d565b919091335f5261020260205260409260ff845f20541680612efd575b156105fe57612e2a61363e565b6001600160a01b038216918215612eec573415612edb5734612e51611d4760985460801c90565b0190612e5b61279e565b8211612eca5794612ea57f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c92610cdd9697612ea0612e9834613b58565b988993612fe8565b6140f5565b51348152602081018590526001600160a01b039290921660408301523391606090a390565b85516304ffa0ff60e51b8152600490fd5b84516318374fd160e21b8152600490fd5b845163d92e233d60e01b8152600490fd5b506001600160a01b0382165f9081528490205460ff16612e1d565b335f5261020260205260ff60405f205416156118ec576125b13433614037565b60405163752a536d60e01b81526020816004817f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f50003066001600160a01b03165afa9081156104fa575f91612fc9575b5060208201916001600160801b03918284511691828214612fc25783612fb561042a612fbd9585848651166130b8565b169052612fe8565b169052565b5050505050565b612fe2915060203d6020116104f3576104e4818361158b565b5f612f85565b6001600160801b0390818111612ffc571690565b604490604051906306dfcc6560e41b8252608060048301526024820152fd5b634e487b7160e01b5f52601260045260245ffd5b8115613039570490565b61301b565b90808202905f19818409908280831092039180830392146130ad57612710908282111561309b577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b60405163227bc15360e01b8152600490fd5b505061271091500490565b9091828202915f198482099383808610950394808603951461312b578483111561309b57829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b505090610a26925061302f565b908160609103126103305780519160406020830151920151610a2681610536565b81835290916001600160fb1b0383116103305760209260051b809284830137010190565b90602082528035602083015260208101358060130b80910361033057604083015260408101356131ac81610525565b6001600160a01b031660608381019190915281013536829003601e19018112156103305701602081359101906001600160401b038111610330578060051b360382136103305760a083608080610a269601520191613159565b9190915f8382019384129112908015821691151617611aae57565b6040516325f56f1160e01b81526001600160a01b0392916060908290819061324b906004830161317d565b03815f877f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b5165af19283156104fa575f915f905f95613371575b50841561331e578161329561260d565b16917f00000000000000000000000048319f97e5da1233c21c48b80097c0fb7a20ff8616821461331757509060205f92600460405180958193634641257d60e01b83525af19081156104fa576132f2925f926132f6575b50613205565b9190565b61331091925060203d6020116104f3576104e4818361158b565b905f6132ec565b9081613324575b50509190565b803b1561033057604051636ee3193160e11b815260048101929092525f908290602490829084905af180156104fa5761335e575b8061331e565b80610e2661336b9261155d565b5f613358565b91945050613397915060603d60601161339f575b61338f818361158b565b810190613138565b93905f613285565b503d613385565b600160ff1b8114611aae575f0390565b6133c5611d4760985460801c90565b5f821261349c57816133d69161285a565b906133e3610cdd83612fe8565b6133f86065549161ffff8360a01c169061303e565b801561349757807f555ee6b2ef9506d870f386c067e47d3689435330b012ad263d8cc3531868654793613436611d476098546001600160801b031690565b8061348157505061347c90925b6001600160a01b03169161345784846140f5565b60405193849384604091949392606082019560018060a01b0316825260208201520152565b0390a1565b61347c92613491920390846130b8565b92613443565b505050565b61042a610cdd916134af6115b9946133a6565b906127e0565b609954906001600160801b0382169182156135ac5760801c6134e96134da82476127e0565b6134e38561256c565b90614141565b9081156135a5576134f982613b21565b93841561359d578261352561291a61042a6115b996610cdd96611d63611dd561042a8d611c009a6127e0565b61352f81876141a7565b60408051878152602081018390527f624ea167e477f9d39f7f4094b9dfe2e6346eb4a7aada54338db51abd554c4b9f9190a161042a61358161357088612fe8565b6098546001600160801b0316612521565b6001600160801b03166001600160801b03196098541617609855565b505f93505050565b505f925050565b505f9150565b604051630156a69560e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa9081156104fa575f9161361f575b501561360d57565b604051630a62fbdb60e11b8152600490fd5b613638915060203d60201161178457611775818361158b565b5f613605565b604051633eb1acf760e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa9081156104fa575f916136aa575b5061369857565b60405163e775715160e01b8152600490fd5b6136c3915060203d60201161178457611775818361158b565b5f613691565b908160a0910312610330578051916020820151916040810151916080606083015192015190565b6136f86142db565b60408301516137056142db565b5f80546001600160a01b0319166001600160a01b03841617905560405133917f2013570c343af8ab14a9778150e381a0fda34ed6368127a95fd5e7210cbec5bf9190819061375390826120e7565b0390a26020830151926137646142db565b61271061ffff8516116137bb576137b3936137816137a693613bb0565b6065805461ffff60a01b191660a09290921b61ffff60a01b1691909117905551614309565b6137ae614339565b61435a565b6115b9614389565b604051638a81d3b360e01b8152600490fd5b6137d56142db565b6137de81613ff0565b6001600160a01b03165f818152610202602081905260409091205460ff16151560011461384957815f5260205260405f20600160ff19825416179055604051600181527fd9c6c3eabe38e3b9a606a66358d8f225489216a59eeba66facefb7d91663526660203392a3565b604051637d5bf36f60e11b8152600490fd5b6001600160a01b037f000000000000000000000000b53a6c402b0d4fb6c7aa59b7d8fbd2e884fbf3bc8116308114918215613899575b505061195557565b5f80516020614a3c8339815191525416141590505f80613891565b906040516138c18161153d565b91546001600160a01b038116835260a01c6020830152565b609a545f948594939091808410801590613a6c575b613a5f5783613a29575f5b609a5f526001600160a01b031661391e5f80516020614a1c83398151915286016138b4565b8051909790613935906001600160a01b031661187f565b9861395a61394e6020809b01516001600160601b031690565b6001600160601b031690565b948381108015613a1f575b613a0d5791600193979a95613984613990939488035b838c0390614141565b809201988703916130b8565b01970193808611801590613a03575b6139f857609a5f5282906139c15f80516020614a1c83398151915287016138b4565b805190890151969992966001600160a01b0390911694600193926139909290916001600160601b039091169061398490880361397b565b945050509250509190565b508185101561399f565b60405163e8722f8f60e01b8152600490fd5b50808b1115613965565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be38401546001600160a01b03166138f9565b505093505050505f905f90565b5084156138ee565b907f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00916002835414613afc5760028355814710613ae4575f918291829182916001600160a01b03165af1613ac6612af1565b5015613ad25760019055565b604051630a12f52160e11b8152600490fd5b60405163cd78605960e01b8152306004820152602490fd5b604051633ee5aeb560e01b8152600490fd5b5f546001600160a01b031633036118ec57565b609854906001600160801b03821681158015613b50575b15613b435750905090565b610a269260801c916130b8565b508015613b38565b6098546001600160801b0381169082158015613ba8575b15613b7957505090565b60801c90613b888282856130b8565b9282156130395709613b975790565b6001810180911115610a265761250d565b508115613b6f565b613bb861363e565b6001600160a01b03168015613c0057606580546001600160a01b03191682179055337faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d35f80a3565b604051630ed1b8b360e31b8152600490fd5b90613c1c82612a1e565b613c29604051918261158b565b8281528092613c3a601f1991612a1e565b0190602036910137565b906030116103305790603090565b906090116103305760300190606090565b9060b0116103305760900190602090565b90939293848311610330578411610330578101920390565b9015612adf5790565b9190811015612adf5760051b0190565b359060208110613cb3575090565b5f199060200360031b1b1690565b96959490612de593613ce2613cf0926060979560808c5260808c01916129fe565b9089820360208b0152611f7b565b9187830360408901526129fe565b906020610a269281815201906129e6565b916020610a269381815201916129fe565b9193929060d154925f925f90613d3581613c12565b97613d3e614649565b935f9760018060a01b037f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa16945b848a10613d7f5750505050505050505050565b60b0613d8f910180998985613c74565b9960409a8d613dcb8d51613db36020918281019061239881611d948c8a8d87612dcd565b805191012091613dc4858b8b613c95565b3590612b8b565b52613dd68184613c44565b9093613df6613df0613de88584613c52565b959093613c63565b90613ca5565b908a3b15610330578b8f5f93613e21915196879485946304512a2360e31b8652888c60048801613cc1565b03816801bc16d674ec8000008d5af19384156104fa57613e7460018e7f64b6e61d93b7a91e8cc4376183ede0997a27b44fd9dd2f30a866b2a5730efdb1958298613e80575b5097019e5192839283613d0f565b0390a101989097613d6c565b80610e26613e8d9261155d565b5f613e66565b90613ea1949593929161468a565b1490565b9192915f915b808310613eb9575050501490565b909192613ed4600191613ecd868587613c95565b3590614991565b93019190613eab565b8160301161033057613df0917f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa6001600160a01b0316613f1b614649565b90613f32613f298486613c52565b96909486613c63565b94813b15610330576801bc16d674ec8000005f94613f9897604051988996879586946304512a2360e31b865260806004870152613f89613f768d60848901906129e6565b60031994858983030160248a0152611f7b565b928684030160448701526129fe565b90606483015203925af19081156104fa577f64b6e61d93b7a91e8cc4376183ede0997a27b44fd9dd2f30a866b2a5730efdb19261347c92613fe1575b5060405191829182613cfe565b613fea9061155d565b5f613fd4565b61020180546001600160a01b0319166001600160a01b03929092169182179055337fda2bcad4d57ac529886ff995d07bce191c08b5424c8f5824de6c73f90cc623d45f80a3565b919061404161363e565b6001600160a01b038316908115610e325780156140e35780614068611d4760985460801c90565b019361407261279e565b85116140d157610cdd9461409491612ea061408c85613b58565b978893612fe8565b60408051918252602082018590525f9082015233907f861a4138e41fb21c121a7dbb1053df465c837fc77380cc7226189a662281be2c90606090a3565b6040516304ffa0ff60e51b8152600490fd5b6040516318374fd160e21b8152600490fd5b6140fe82612fe8565b609854906141166001600160801b03918284166125c3565b16906001600160801b0319161760985560018060a01b03165f52609c60205260405f20908154019055565b90808210156127c6575090565b609a5490600160401b821015611558576001820180609a55821015612adf57609a5f52805160209091015160a01b6001600160a01b0319166001600160a01b0391909116175f80516020614a1c83398151915290910155565b9081158015614286575b61427457609a548061423e57505f905b6001600160a01b03918216928301928310611aae5781831161421e576115b992916142096141f161421993614831565b916141fa6115ac565b94166001600160a01b03168452565b6001600160601b03166020830152565b61414e565b6040516306dfcc6560e41b815260a0600482015260248101849052604490fd5b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be301546001600160a01b0316906141c1565b604051632ec8835b60e21b8152600490fd5b5080156141b1565b60018060a01b03165f52609c60205260405f20908154818103908111611aae576142b89255612fe8565b609854906001600160801b03908183160316906001600160801b03191617609855565b60ff5f80516020614a5c8339815191525460401c16156142f757565b604051631afcd79f60e31b8152600490fd5b6143116142db565b801561432757600181016143225750565b609d55565b6040516331278a8760e01b8152600490fd5b6143416142db565b6801bc16d674ec80000061435361279e565b1061432757565b6143626142db565b6001600160a01b0316806143735750565b61016a80546001600160a01b0319169091179055565b6143916142db565b6143996142db565b6143a16142db565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055633b9aca0034106143da576125b13430614037565b60405163ea2559bb60e01b8152600490fd5b90816020910312610330575160ff811681036103305790565b6040516352d1902d60e01b81529290916020846004816001600160a01b0387165afa5f948161448f575b5061445557604051634c9c8ce360e01b81526001600160a01b0384166004820152602490fd5b90915f80516020614a3c8339815191528403614476576115b9929350614864565b604051632a87526960e21b815260048101859052602490fd5b6144a991955060203d6020116104f3576104e4818361158b565b935f61442f565b6001600160a01b0381165f908152610137602052604090206144d1906124e8565b906001600160801b036144eb83516001600160801b031690565b1615614645576107da6145229161450061363e565b61450984612f38565b6001600160a01b03165f908152609c6020526040902090565b604051631331885160e31b8152602092916001600160a01b0384836004817f000000000000000000000000e8822246f8864da92015813a39ae776087fb1cd585165afa9182156104fa576145858693614593926145bf965f9261462d575061303e565b94516001600160801b031690565b6040516303d1689d60e11b81526001600160801b03909116600482015292839190829081906024820190565b03917f0000000000000000000000002a261e60fb14586b474c208b1b7ac6d0f5000306165afa9283156104fa575f9361460e575b5050106145fc57565b604051633684c65960e01b8152600490fd5b614625929350803d106104f3576104e4818361158b565b905f806145f3565b61094c919250863d88116104f3576104e4818361158b565b5050565b604051600160f81b60208201525f60218201523060601b602c82015260208152610a268161153d565b5f198114611aae5760010190565b35610a2681610536565b929391909180519361469c848661285a565b6146a58761284c565b036146e7576146b386613c12565b945f8094885f965f5b82811061471f5750501591506146f9905057505050036146e7576146e3915f190190612b8b565b5190565b604051631a8a024960e11b8152600490fd5b9195509293501590506147115750506146e390612b7e565b61471b9250613c8c565b3590565b8a868610156147d2575061475161474c8261474361473c89614672565b988c612b8b565b51955b87613c95565b614680565b156147b7578a8686101561479657506147826001929361477a61477388614672565b978b612b8b565b515b90614991565b61478c828d612b8b565b5201908a916146bc565b92614782906147b1846147ab60019691614672565b96612b8b565b5161477c565b61478260019293613ecd6147ca8c614672565b9b8d8b613c95565b9161474c826147e8836147ab6147519591614672565b5195614746565b609a54806147fc57505f90565b609a5f527f44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be301546001600160a01b031661187f565b6001600160601b0390818111614845571690565b604490604051906306dfcc6560e41b8252606060048301526024820152fd5b90813b156148e5575f80516020614a3c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156148ca576125b1916149b2565b5050346148d357565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b604051630156a69560e11b81523060048201526020816024817f0000000000000000000000006b5815467da09daa7dc83db21c9239d98bb487b56001600160a01b03165afa9081156104fa575f91614972575b5061496057565b6040516389a1dc6360e01b8152600490fd5b61498b915060203d60201161178457611775818361158b565b5f614959565b818110156149a5575f5260205260405f2090565b905f5260205260405f2090565b5f80610a2693602081519101845af46149c9612af1565b91906149df5750805115613ad257805190602001fd5b81511580614a12575b6149f0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156149e856fe44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220763ded4fbe3b0bada35a1a872c3cfedea0cd0462937e0023112665f32f1926fa64736f6c63430008160033

Verified Source Code Full Match

Compiler: v0.8.22+commit.4fc1097e EVM: shanghai Optimization: Yes (200 runs)
Multicall.sol 33 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity =0.8.22;

import '../interfaces/IMulticall.sol';

/**
 * @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
 */
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++) {
      (bool success, bytes memory result) = address(this).delegatecall(data[i]);

      if (!success) {
        // Next 5 lines from https://ethereum.stackexchange.com/a/83577
        if (result.length < 68) revert();
        assembly {
          result := add(result, 0x04)
        }
        revert(abi.decode(result, (string)));
      }

      results[i] = result;
    }
  }
}
Errors.sol 57 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 InsufficientAssets();
  error CapacityExceeded();
  error InvalidCapacity();
  error InvalidSecurityDeposit();
  error InvalidFeeRecipient();
  error InvalidFeePercent();
  error NotHarvested();
  error NotCollateralized();
  error Collateralized();
  error InvalidProof();
  error LowLtv();
  error RedemptionExceeded();
  error InvalidPosition();
  error InvalidLtv();
  error InvalidHealthFactor();
  error InvalidReceivedAssets();
  error InvalidTokenMeta();
  error UpgradeFailed();
  error InvalidValidator();
  error InvalidValidators();
  error WhitelistAlreadyUpdated();
  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 InvalidRedeemFromLtvPercent();
  error InvalidLiqThresholdPercent();
  error InvalidLiqBonusPercent();
  error InvalidLtvPercent();
  error InvalidCheckpointIndex();
  error InvalidCheckpointValue();
  error MaxOraclesExceeded();
  error ClaimTooEarly();
}
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
  ) internal 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
  ) internal 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();
    }

    // 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
    uint256 checkpointShares;
    uint256 sharesDelta;
    while (true) {
      unchecked {
        // cannot underflow as prevTotalTickets <= positionTicket
        checkpointShares = currTotalTickets - prevTotalTickets;
        // cannot underflow as positionShares > burnedShares while in the loop
        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 || assets == 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)
    }
  }
}
IEthVault.sol 50 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.22;

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 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 the EthVault contract. Must transfer security deposit together with a call.
   * @param params The encoded parameters for initializing the EthVault contract
   */
  function initialize(bytes calldata params) external payable;
}
IVaultFee.sol 37 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 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;
}
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);
}
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);
}
IVaultAdmin.sol 29 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 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;
}
IVaultState.sol 91 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 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 Queued Shares
   * @return The total number of shares queued for exit
   */
  function queuedShares() external view returns (uint128);

  /**
   * @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 shares to assets
   * @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 assets to shares
   * @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;
}
VaultFee.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 {IVaultFee} from '../../interfaces/IVaultFee.sol';
import {IKeeperRewards} from '../../interfaces/IKeeperRewards.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 %

  /// @inheritdoc IVaultFee
  address public override feeRecipient;

  /// @inheritdoc IVaultFee
  uint16 public override feePercent;

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

  /**
   * @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();

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

  /**
   * @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 {
    if (_feePercent > _maxFeePercent) revert Errors.InvalidFeePercent();

    _setFeeRecipient(_feeRecipient);
    feePercent = _feePercent;
  }

  /**
   * @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 78 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 override returns (int256, bool) {
    (int256 totalAssetsDelta, uint256 unlockedMevDelta, bool 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);
      }
      return (totalAssetsDelta, harvested);
    }

    // execution rewards are always equal to what was accumulated in own MEV escrow
    return (totalAssetsDelta + int256(IOwnMevEscrow(_mevEscrow).harvest()), harvested);
  }

  /**
   * @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;
}
IEthPrivVault.sol 15 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.22;

import {IVaultWhitelist} from './IVaultWhitelist.sol';
import {IEthVault} from './IEthVault.sol';

/**
 * @title IEthPrivVault
 * @author StakeWise
 * @notice Defines the interface for the EthPrivVault contract
 */
interface IEthPrivVault is IEthVault, IVaultWhitelist {

}
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);
}
IVaultOsToken.sol 127 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
   * @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.
   * @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.
   * @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;
}
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);
}
EthVault.sol 143 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.22;

import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {IVaultVersion} from '../../interfaces/IVaultVersion.sol';
import {IVaultEnterExit} from '../../interfaces/IVaultEnterExit.sol';
import {IEthVault} from '../../interfaces/IEthVault.sol';
import {IEthVaultFactory} from '../../interfaces/IEthVaultFactory.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} from '../modules/VaultVersion.sol';
import {VaultImmutables} from '../modules/VaultImmutables.sol';
import {VaultState} from '../modules/VaultState.sol';
import {VaultEnterExit} 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
{
  /**
   * @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
   * @param _validatorsRegistry The contract address used for registering validators in beacon chain
   * @param osTokenVaultController The address of the OsTokenVaultController contract
   * @param osTokenConfig The address of the OsTokenConfig contract
   * @param sharedMevEscrow The address of the shared MEV escrow
   * @param exitedAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking
   */
  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor(
    address _keeper,
    address _vaultsRegistry,
    address _validatorsRegistry,
    address osTokenVaultController,
    address osTokenConfig,
    address sharedMevEscrow,
    uint256 exitedAssetsClaimDelay
  )
    VaultImmutables(_keeper, _vaultsRegistry, _validatorsRegistry)
    VaultEnterExit(exitedAssetsClaimDelay)
    VaultOsToken(osTokenVaultController, osTokenConfig)
    VaultMev(sharedMevEscrow)
  {
    _disableInitializers();
  }

  /// @inheritdoc IEthVault
  function initialize(bytes calldata params) external payable virtual override initializer {
    __EthVault_init(
      IEthVaultFactory(msg.sender).vaultAdmin(),
      IEthVaultFactory(msg.sender).ownMevEscrow(),
      abi.decode(params, (EthVaultInitParams))
    );
  }

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

  /// @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 1;
  }

  /**
   * @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;
}
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;
}
IKeeperRewards.sol 202 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;
}
IOsTokenConfig.sol 88 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 redeemFromLtvPercent The LTV allowed to redeem from
   * @param redeemToLtvPercent The LTV to redeem up to
   * @param liqThresholdPercent The new liquidation threshold percent value
   * @param liqBonusPercent The new liquidation bonus percent value
   * @param ltvPercent The new loan-to-value (LTV) percent value
   */
  event OsTokenConfigUpdated(
    uint16 redeemFromLtvPercent,
    uint16 redeemToLtvPercent,
    uint16 liqThresholdPercent,
    uint16 liqBonusPercent,
    uint16 ltvPercent
  );

  /**
   * @notice The OsToken minting and liquidating configuration values
   * @param redeemFromLtvPercent The osToken redemptions are allowed when position LTV goes above this value
   * @param redeemToLtvPercent The osToken redeemed value cannot decrease LTV below this value
   * @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 {
    uint16 redeemFromLtvPercent;
    uint16 redeemToLtvPercent;
    uint16 liqThresholdPercent;
    uint16 liqBonusPercent;
    uint16 ltvPercent;
  }

  /**
   * @notice The osToken redemptions are allowed when position LTV goes above this value
   * @return The minimal LTV before redemption start
   */
  function redeemFromLtvPercent() external view returns (uint256);

  /**
   * @notice The osToken redeemed value cannot decrease LTV below this value
   * @return The maximal LTV after the redemption
   */
  function redeemToLtvPercent() external view returns (uint256);

  /**
   * @notice The liquidation threshold percent used to calculate health factor for OsToken position
   * @return The liquidation threshold percent value
   */
  function liqThresholdPercent() external view returns (uint256);

  /**
   * @notice The minimal bonus percent that liquidator earns on OsToken position liquidation
   * @return The minimal liquidation bonus percent value
   */
  function liqBonusPercent() external view returns (uint256);

  /**
   * @notice The percent used to calculate how much user can mint OsToken shares
   * @return The loan-to-value (LTV) percent value
   */
  function ltvPercent() external view returns (uint256);

  /**
   * @notice Returns the OsToken minting and liquidating configuration values
   * @return redeemFromLtvPercent The LTV allowed to redeem from
   * @return redeemToLtvPercent The LTV to redeem up to
   * @return liqThresholdPercent The liquidation threshold percent value
   * @return liqBonusPercent The liquidation bonus percent value
   * @return ltvPercent The loan-to-value (LTV) percent value
   */
  function getConfig() external view returns (uint256, uint256, uint256, uint256, uint256);

  /**
   * @notice Updates the OsToken minting and liquidating configuration values. Can only be called by the owner.
   * @param config The new OsToken configuration
   */
  function updateConfig(Config memory config) external;
}
VaultAdmin.sol 49 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);
  }

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

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

  /**
   * @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;
}
VaultState.sol 269 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;

  /// @inheritdoc IVaultState
  uint128 public override queuedShares;
  uint128 internal _unclaimedAssets;

  ExitQueue.History internal _exitQueue;
  mapping(bytes32 => uint256) internal _exitRequests;
  mapping(address => uint256) internal _balances;

  uint256 private _capacity;

  /// @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 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) + _unclaimedAssets;
      return vaultAssets > reservedAssets ? vaultAssets - reservedAssets : 0;
    }
  }

  /// @inheritdoc IVaultState
  function isStateUpdateRequired() external view 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);

    // process total assets delta if it has changed
    if (totalAssetsDelta != 0) _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 {
    // SLOAD to memory
    uint256 newTotalAssets = _totalAssets;
    if (totalAssetsDelta < 0) {
      // add penalty to total assets
      newTotalAssets -= uint256(-totalAssetsDelta);

      // update state
      _totalAssets = SafeCast.toUint128(newTotalAssets);
      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 1 day).
          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) {
    // SLOAD to memory
    uint256 _queuedShares = queuedShares;
    if (_queuedShares == 0) return 0;

    // calculate the amount of assets that can be exited
    uint256 unclaimedAssets = _unclaimedAssets;
    uint256 exitedAssets = Math.min(
      _vaultAssets() - unclaimedAssets,
      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(unclaimedAssets + 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 The total assets delta after harvest
   * @return `true` when the rewards were harvested, `false` otherwise
   */
  function _harvestAssets(
    IKeeperRewards.HarvestParams calldata harvestParams
  ) internal virtual returns (int256, bool);

  /**
	 * @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 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;
}
IVaultEnterExit.sol 124 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 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
   */
  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 leftShares The number of shares that are still in the queue
   * @return claimedShares The number of claimed shares
   * @return claimedAssets The number of claimed assets
   */
  function calculateExitedAssets(
    address receiver,
    uint256 positionTicket,
    uint256 timestamp,
    uint256 exitQueueIndex
  ) external view returns (uint256 leftShares, uint256 claimedShares, uint256 claimedAssets);

  /**
   * @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 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 newPositionTicket The new exit queue ticket in case not all the shares were burned. Otherwise 0.
   * @return claimedShares The number of shares claimed
   * @return claimedAssets The number of assets claimed
   */
  function claimExitedAssets(
    uint256 positionTicket,
    uint256 timestamp,
    uint256 exitQueueIndex
  ) external returns (uint256 newPositionTicket, uint256 claimedShares, uint256 claimedAssets);

  /**
   * @notice Redeems assets from the Vault by utilising what has not been staked yet. Can only be called when vault is not collateralized.
   * @param shares The number of shares to burn
   * @param receiver The address that will receive assets
   * @return assets The number of assets withdrawn
   */
  function redeem(uint256 shares, address receiver) external returns (uint256 assets);
}
IVaultWhitelist.sol 53 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.22;

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

/**
 * @title IVaultWhitelist
 * @author StakeWise
 * @notice Defines the interface for the VaultWhitelist contract
 */
interface IVaultWhitelist is IVaultAdmin {
  /**
   * @notice Event emitted on whitelist update
   * @param caller The address of the function caller
   * @param account The address of the account updated
   * @param approved Whether account is approved or not
   */
  event WhitelistUpdated(address indexed caller, address indexed account, bool approved);

  /**
   * @notice Event emitted when whitelister address is updated
   * @param caller The address of the function caller
   * @param whitelister The address of the new whitelister
   */
  event WhitelisterUpdated(address indexed caller, address indexed whitelister);

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

  /**
   * @notice Checks whether account is whitelisted or not
   * @param account The account to check
   * @return `true` for the whitelisted account, `false` otherwise
   */
  function whitelistedAccounts(address account) external view returns (bool);

  /**
   * @notice Add or remove account from the whitelist. Can only be called by the whitelister.
   * @param account The account to add or remove from the whitelist
   * @param approved Whether account should be whitelisted or not
   */
  function updateWhitelist(address account, bool approved) external;

  /**
   * @notice Used to update the whitelister. Can only be called by the Vault admin.
   * @param _whitelister The address of the new whitelister
   */
  function setWhitelister(address _whitelister) external;
}
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;
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

    /**
     * @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 AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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
     * {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
        }
        (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
     */
    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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
IEthVaultFactory.sol 53 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);
}
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;
}
IVaultEthStaking.sol 42 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 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);
}
IVaultValidators.sol 88 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 validator registration
   * @param publicKey The public key of the validator that was registered
   */
  event ValidatorRegistered(bytes publicKey);

  /**
   * @notice Event emitted on keys manager address update
   * @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
   * @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 The Vault keys manager address
   * @return The address that can update validators merkle tree root
   */
  function keysManager() external view returns (address);

  /**
   * @notice The Vault validators root
   * @return The merkle tree root to use for verifying validators deposit data
   */
  function validatorsRoot() external view returns (bytes32);

  /**
   * @notice The Vault validator index
   * @return The index of the next validator to be registered in the current deposit data file
   */
  function validatorIndex() external view returns (uint256);

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

  /**
   * @notice Function for registering multiple validators
   * @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(
    IKeeperValidators.ApprovalParams calldata keeperParams,
    uint256[] calldata indexes,
    bool[] calldata proofFlags,
    bytes32[] calldata proof
  ) external;

  /**
   * @notice Function for updating the keys manager. Can only be called by the admin.
   * @param _keysManager The new keys manager address
   */
  function setKeysManager(address _keysManager) external;

  /**
   * @notice Function for updating the validators merkle tree root. Can only be called by the keys manager.
   * @param _validatorsRoot The new validators merkle tree root
   */
  function setValidatorsRoot(bytes32 _validatorsRoot) external;
}
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 {IVaultEnterExit} from '../../interfaces/IVaultEnterExit.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultImmutables} from './VaultImmutables.sol';
import {VaultEnterExit} from './VaultEnterExit.sol';
import {VaultState} from './VaultState.sol';

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

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

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

  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
   */
  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor(address osTokenVaultController, address osTokenConfig) {
    _osTokenVaultController = IOsTokenVaultController(osTokenVaultController);
    _osTokenConfig = IOsTokenConfig(osTokenConfig);
  }

  /// @inheritdoc IVaultOsToken
  function osTokenPositions(address user) external 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
  ) external override returns (uint256 assets) {
    _checkCollateralized();
    _checkHarvested();

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

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

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

    // calculate and validate LTV
    if (
      Math.mulDiv(
        convertToAssets(_balances[msg.sender]),
        _osTokenConfig.ltvPercent(),
        _maxPercent
      ) < _osTokenVaultController.convertToAssets(position.shares)
    ) {
      revert Errors.LowLtv();
    }

    // update state
    _positions[msg.sender] = position;

    // emit event
    emit OsTokenMinted(msg.sender, receiver, assets, 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 -= SafeCast.toUint128(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 {
    (uint256 burnedShares, uint256 receivedAssets) = _redeemOsToken(
      owner,
      receiver,
      osTokenShares,
      false
    );
    emit OsTokenRedeemed(msg.sender, owner, receiver, osTokenShares, burnedShares, receivedAssets);
  }

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

  /// @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 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);

    // SLOAD to memory
    (
      uint256 redeemFromLtvPercent,
      uint256 redeemToLtvPercent,
      uint256 liqThresholdPercent,
      uint256 liqBonusPercent,

    ) = _osTokenConfig.getConfig();

    // calculate received assets
    if (isLiquidation) {
      receivedAssets = Math.mulDiv(
        _osTokenVaultController.convertToAssets(osTokenShares),
        liqBonusPercent,
        _maxPercent
      );
    } else {
      receivedAssets = _osTokenVaultController.convertToAssets(osTokenShares);
    }

    {
      // check whether received assets are valid
      uint256 depositedAssets = convertToAssets(_balances[owner]);
      if (receivedAssets > depositedAssets || receivedAssets > withdrawableAssets()) {
        revert Errors.InvalidReceivedAssets();
      }

      uint256 mintedAssets = _osTokenVaultController.convertToAssets(position.shares);
      if (isLiquidation) {
        // check health factor violation in case of liquidation
        if (
          Math.mulDiv(depositedAssets * _wad, liqThresholdPercent, mintedAssets * _maxPercent) >=
          _hfLiqThreshold
        ) {
          revert Errors.InvalidHealthFactor();
        }
      } else if (
        // check ltv violation in case of redemption
        Math.mulDiv(depositedAssets, redeemFromLtvPercent, _maxPercent) > mintedAssets
      ) {
        revert Errors.InvalidLtv();
      }
    }

    // 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
    unchecked {
      _totalAssets -= SafeCast.toUint128(receivedAssets);
    }

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

    // check ltv violation in case of redemption
    if (
      !isLiquidation &&
      Math.mulDiv(convertToAssets(_balances[owner]), redeemToLtvPercent, _maxPercent) >
      _osTokenVaultController.convertToAssets(position.shares)
    ) {
      revert Errors.RedemptionExceeded();
    }

    // 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 (
      Math.mulDiv(convertToAssets(_balances[user]), _osTokenConfig.ltvPercent(), _maxPercent) <
      _osTokenVaultController.convertToAssets(position.shares)
    ) {
      revert Errors.LowLtv();
    }
  }

  /**
   * @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 67 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;
}
IKeeperValidators.sol 95 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;
}
EthPrivVault.sol 105 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 {IEthPrivVault} from '../../interfaces/IEthPrivVault.sol';
import {IEthVaultFactory} from '../../interfaces/IEthVaultFactory.sol';
import {IVaultEthStaking} from '../../interfaces/IVaultEthStaking.sol';
import {IVaultVersion} from '../../interfaces/IVaultVersion.sol';
import {Errors} from '../../libraries/Errors.sol';
import {VaultEthStaking} from '../modules/VaultEthStaking.sol';
import {VaultWhitelist} from '../modules/VaultWhitelist.sol';
import {VaultVersion} from '../modules/VaultVersion.sol';
import {EthVault} from './EthVault.sol';

/**
 * @title EthPrivVault
 * @author StakeWise
 * @notice Defines the Ethereum staking Vault with whitelist
 */
contract EthPrivVault is Initializable, EthVault, VaultWhitelist, IEthPrivVault {
  /**
   * @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
   * @param _validatorsRegistry The contract address used for registering validators in beacon chain
   * @param osTokenVaultController The address of the OsTokenVaultController contract
   * @param osTokenConfig The address of the OsTokenConfig contract
   * @param sharedMevEscrow The address of the shared MEV escrow
   * @param exitingAssetsClaimDelay The minimum delay after which the assets can be claimed after joining the exit queue
   */
  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor(
    address _keeper,
    address _vaultsRegistry,
    address _validatorsRegistry,
    address osTokenVaultController,
    address osTokenConfig,
    address sharedMevEscrow,
    uint256 exitingAssetsClaimDelay
  )
    EthVault(
      _keeper,
      _vaultsRegistry,
      _validatorsRegistry,
      osTokenVaultController,
      osTokenConfig,
      sharedMevEscrow,
      exitingAssetsClaimDelay
    )
  {}

  /// @inheritdoc IEthVault
  function initialize(
    bytes calldata params
  ) external payable virtual override(IEthVault, EthVault) initializer {
    address admin = IEthVaultFactory(msg.sender).vaultAdmin();
    __EthVault_init(
      admin,
      IEthVaultFactory(msg.sender).ownMevEscrow(),
      abi.decode(params, (EthVaultInitParams))
    );
    // whitelister is initially set to admin address
    __VaultWhitelist_init(admin);
  }

  /// @inheritdoc IVaultEthStaking
  function deposit(
    address receiver,
    address referrer
  ) public payable virtual override(IVaultEthStaking, VaultEthStaking) returns (uint256 shares) {
    if (!(whitelistedAccounts[msg.sender] && whitelistedAccounts[receiver])) {
      revert Errors.AccessDenied();
    }
    return super.deposit(receiver, referrer);
  }

  /**
   * @dev Function for depositing using fallback function
   */
  receive() external payable virtual override {
    if (!whitelistedAccounts[msg.sender]) revert Errors.AccessDenied();
    _deposit(msg.sender, msg.value, address(0));
  }

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

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

  /**
   * @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;
}
Math.sol 415 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return 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.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev 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 {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 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 prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

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

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, 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 {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

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

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            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^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // 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^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice 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) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

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

    /**
     * @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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @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 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @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;
    }
}
VaultEnterExit.sol 213 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 {IKeeperRewards} from '../../interfaces/IKeeperRewards.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 redeem(
    uint256 shares,
    address receiver
  ) public virtual override returns (uint256 assets) {
    _checkNotCollateralized();
    if (shares == 0) revert Errors.InvalidShares();
    if (receiver == address(0)) revert Errors.ZeroAddress();

    // calculate amount of assets to burn
    assets = convertToAssets(shares);

    // reverts in case there are not enough withdrawable assets
    if (assets > withdrawableAssets()) revert Errors.InsufficientAssets();

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

    // burn owner shares
    _burnShares(msg.sender, shares);

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

    emit Redeemed(msg.sender, receiver, assets, shares);
  }

  /// @inheritdoc IVaultEnterExit
  function enterExitQueue(
    uint256 shares,
    address receiver
  ) public virtual override returns (uint256 positionTicket) {
    _checkCollateralized();
    if (shares == 0) revert Errors.InvalidShares();
    if (receiver == address(0)) revert Errors.ZeroAddress();

    // SLOAD to memory
    uint256 _queuedShares = queuedShares;

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

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

    // reverts if owner does not have enough shares
    _balances[msg.sender] -= shares;

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

    emit ExitQueueEntered(msg.sender, receiver, positionTicket, shares);
  }

  /// @inheritdoc IVaultEnterExit
  function calculateExitedAssets(
    address receiver,
    uint256 positionTicket,
    uint256 timestamp,
    uint256 exitQueueIndex
  )
    public
    view
    override
    returns (uint256 leftShares, uint256 claimedShares, uint256 claimedAssets)
  {
    uint256 requestedShares = _exitRequests[
      keccak256(abi.encode(receiver, timestamp, positionTicket))
    ];

    // calculate exited shares and assets
    (claimedShares, claimedAssets) = _exitQueue.calculateExitedAssets(
      exitQueueIndex,
      positionTicket,
      requestedShares
    );
    leftShares = requestedShares - claimedShares;
  }

  /// @inheritdoc IVaultEnterExit
  function claimExitedAssets(
    uint256 positionTicket,
    uint256 timestamp,
    uint256 exitQueueIndex
  )
    external
    override
    returns (uint256 newPositionTicket, uint256 claimedShares, uint256 claimedAssets)
  {
    if (block.timestamp < timestamp + _exitingAssetsClaimDelay) revert Errors.ClaimTooEarly();
    bytes32 queueId = keccak256(abi.encode(msg.sender, timestamp, positionTicket));

    // calculate exited shares and assets
    uint256 leftShares;
    (leftShares, claimedShares, claimedAssets) = calculateExitedAssets(
      msg.sender,
      positionTicket,
      timestamp,
      exitQueueIndex
    );
    // nothing to claim
    if (claimedShares == 0) return (positionTicket, claimedShares, claimedAssets);

    // clean up current exit request
    delete _exitRequests[queueId];

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

    // transfer assets to the receiver
    _unclaimedAssets -= SafeCast.toUint128(claimedAssets);
    _transferVaultAssets(msg.sender, claimedAssets);
    emit ExitedAssetsClaimed(msg.sender, positionTicket, newPositionTicket, claimedAssets);
  }

  /**
   * @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 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;
}
VaultWhitelist.sol 70 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.22;

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

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

  /// @inheritdoc IVaultWhitelist
  mapping(address => bool) public override whitelistedAccounts;

  /// @inheritdoc IVaultWhitelist
  function updateWhitelist(address account, bool approved) external override {
    if (msg.sender != whitelister) revert Errors.AccessDenied();
    _updateWhitelist(account, approved);
  }

  /// @inheritdoc IVaultWhitelist
  function setWhitelister(address _whitelister) external override {
    _checkAdmin();
    _setWhitelister(_whitelister);
  }

  /**
   * @notice Internal function for updating whitelist
   * @param account The address of the account to update
   * @param approved Defines whether account is added to the whitelist or removed
   */
  function _updateWhitelist(address account, bool approved) private {
    if (whitelistedAccounts[account] == approved) revert Errors.WhitelistAlreadyUpdated();
    whitelistedAccounts[account] = approved;
    emit WhitelistUpdated(msg.sender, account, approved);
  }

  /**
   * @dev Internal function for updating the whitelister externally or from the initializer
   * @param _whitelister The address of the new whitelister
   */
  function _setWhitelister(address _whitelister) private {
    // update whitelister address
    whitelister = _whitelister;
    emit WhitelisterUpdated(msg.sender, _whitelister);
  }

  /**
   * @dev Initializes the VaultWhitelist contract
   * @param _whitelister The address of the whitelister
   */
  function __VaultWhitelist_init(address _whitelister) internal onlyInitializing {
    _setWhitelister(_whitelister);
    _updateWhitelist(_whitelister, 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;
}
IValidatorsRegistry.sol 23 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);
}
VaultEthStaking.sol 154 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 {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 {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,
  ReentrancyGuardUpgradeable,
  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 VaultValidators
  function _registerSingleValidator(bytes calldata validator) internal virtual override {
    bytes calldata publicKey = validator[:48];
    IEthValidatorsRegistry(_validatorsRegistry).deposit{value: _validatorDeposit()}(
      publicKey,
      _withdrawalCredentials(),
      validator[48:144],
      bytes32(validator[144:_validatorLength])
    );

    emit ValidatorRegistered(publicKey);
  }

  /// @inheritdoc VaultValidators
  function _registerMultipleValidators(
    bytes calldata validators,
    uint256[] calldata indexes
  ) internal virtual override returns (bytes32[] memory leaves) {
    // SLOAD to memory
    uint256 currentValIndex = validatorIndex;

    uint256 startIndex;
    uint256 endIndex;
    bytes calldata validator;
    bytes calldata publicKey;
    uint256 validatorsCount = indexes.length;
    leaves = new bytes32[](validatorsCount);
    uint256 validatorDeposit = _validatorDeposit();
    bytes memory withdrawalCreds = _withdrawalCredentials();

    for (uint256 i = 0; i < validatorsCount; i++) {
      unchecked {
        // cannot realistically overflow
        endIndex += _validatorLength;
      }
      validator = validators[startIndex:endIndex];
      leaves[indexes[i]] = keccak256(
        bytes.concat(keccak256(abi.encode(validator, currentValIndex)))
      );
      publicKey = validator[:48];
      // slither-disable-next-line arbitrary-send-eth
      IEthValidatorsRegistry(_validatorsRegistry).deposit{value: validatorDeposit}(
        publicKey,
        withdrawalCreds,
        validator[48:144],
        bytes32(validator[144:_validatorLength])
      );
      startIndex = endIndex;
      unchecked {
        // cannot realistically overflow
        ++currentValIndex;
      }
      emit ValidatorRegistered(publicKey);
    }
  }

  /// @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);
  }

  /// @inheritdoc VaultValidators
  function _validatorDeposit() internal pure override returns (uint256) {
    return 32 ether;
  }

  /**
   * @dev Initializes the VaultEthStaking contract
   */
  function __VaultEthStaking_init() internal onlyInitializing {
    __ReentrancyGuard_init();

    // 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;
}
VaultImmutables.sol 58 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
 */
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;

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

  /**
   * @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
   * @param validatorsRegistry The contract address used for registering validators in beacon chain
   */
  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor(address keeper, address vaultsRegistry, address validatorsRegistry) {
    _keeper = keeper;
    _vaultsRegistry = vaultsRegistry;
    _validatorsRegistry = validatorsRegistry;
  }

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

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

  /**
   * @dev Internal method for checking whether the vault is not collateralized
   */
  function _checkNotCollateralized() internal view {
    if (IKeeperRewards(_keeper).isCollateralized(address(this))) revert Errors.Collateralized();
  }
}
VaultValidators.sol 200 lines
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.22;

import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import {MerkleProof} from '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
import {IKeeperValidators} from '../../interfaces/IKeeperValidators.sol';
import {IVaultValidators} from '../../interfaces/IVaultValidators.sol';
import {Errors} from '../../libraries/Errors.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,
  VaultAdmin,
  VaultState,
  IVaultValidators
{
  uint256 internal constant _validatorLength = 176;

  /// @inheritdoc IVaultValidators
  bytes32 public override validatorsRoot;

  /// @inheritdoc IVaultValidators
  uint256 public override validatorIndex;

  address private _keysManager;

  /// @inheritdoc IVaultValidators
  function keysManager() public view override returns (address) {
    // SLOAD to memory
    address keysManager_ = _keysManager;
    // if keysManager is not set, use admin address
    return keysManager_ == address(0) ? admin : keysManager_;
  }

  /// @inheritdoc IVaultValidators
  function registerValidator(
    IKeeperValidators.ApprovalParams calldata keeperParams,
    bytes32[] calldata proof
  ) external override {
    _checkHarvested();

    // get approval from oracles
    IKeeperValidators(_keeper).approveValidators(keeperParams);

    // check enough withdrawable assets
    if (withdrawableAssets() < _validatorDeposit()) revert Errors.InsufficientAssets();

    // check validator length is valid
    if (keeperParams.validators.length != _validatorLength) revert Errors.InvalidValidator();

    // SLOAD to memory
    uint256 currentIndex = validatorIndex;

    // check matches merkle root and next validator index
    if (
      !MerkleProof.verifyCalldata(
        proof,
        validatorsRoot,
        keccak256(bytes.concat(keccak256(abi.encode(keeperParams.validators, currentIndex))))
      )
    ) {
      revert Errors.InvalidProof();
    }

    // register validator
    _registerSingleValidator(keeperParams.validators);

    // increment index for the next validator
    unchecked {
      // cannot realistically overflow
      validatorIndex = currentIndex + 1;
    }
  }

  /// @inheritdoc IVaultValidators
  function registerValidators(
    IKeeperValidators.ApprovalParams calldata keeperParams,
    uint256[] calldata indexes,
    bool[] calldata proofFlags,
    bytes32[] calldata proof
  ) external override {
    _checkHarvested();

    // get approval from oracles
    IKeeperValidators(_keeper).approveValidators(keeperParams);

    // check enough withdrawable assets
    uint256 validatorsCount = indexes.length;
    if (withdrawableAssets() < _validatorDeposit() * validatorsCount) {
      revert Errors.InsufficientAssets();
    }

    // check validators length is valid
    unchecked {
      if (
        validatorsCount == 0 || validatorsCount * _validatorLength != keeperParams.validators.length
      ) {
        revert Errors.InvalidValidators();
      }
    }

    // check matches merkle root and next validator index
    if (
      !MerkleProof.multiProofVerifyCalldata(
        proof,
        proofFlags,
        validatorsRoot,
        _registerMultipleValidators(keeperParams.validators, indexes)
      )
    ) {
      revert Errors.InvalidProof();
    }

    // increment index for the next validator
    unchecked {
      // cannot realistically overflow
      validatorIndex += validatorsCount;
    }
  }

  /// @inheritdoc IVaultValidators
  function setKeysManager(address keysManager_) external override {
    _checkAdmin();
    if (keysManager_ == address(0)) revert Errors.ZeroAddress();
    // update keysManager address
    _keysManager = keysManager_;
    emit KeysManagerUpdated(msg.sender, keysManager_);
  }

  /// @inheritdoc IVaultValidators
  function setValidatorsRoot(bytes32 _validatorsRoot) external override {
    if (msg.sender != keysManager()) revert Errors.AccessDenied();
    _setValidatorsRoot(_validatorsRoot);
  }

  /**
   * @dev Internal function for updating the validators root externally or from the initializer
   * @param _validatorsRoot The new validators merkle tree root
   */
  function _setValidatorsRoot(bytes32 _validatorsRoot) private {
    validatorsRoot = _validatorsRoot;
    // reset validator index on every root update
    validatorIndex = 0;
    emit ValidatorsRootUpdated(msg.sender, _validatorsRoot);
  }

  /**
   * @dev Internal function for calculating Vault withdrawal credentials
   * @return The credentials used for the validators withdrawals
   */
  function _withdrawalCredentials() internal view returns (bytes memory) {
    return abi.encodePacked(bytes1(0x01), bytes11(0x0), address(this));
  }

  /**
   * @dev Internal function for registering single validator. Must emit ValidatorRegistered event.
   * @param validator The concatenation of the validator public key, signature and deposit data root
   */
  function _registerSingleValidator(bytes calldata validator) internal virtual;

  /**
   * @dev Internal function for registering multiple validators. Must emit ValidatorRegistered event for every validator.
   * @param validators The concatenation of the validators' public key, signature and deposit data root
   * @param indexes The indexes of the leaves for the merkle tree multi proof verification
   * @return leaves The leaves used for the merkle tree multi proof verification
   */
  function _registerMultipleValidators(
    bytes calldata validators,
    uint256[] calldata indexes
  ) internal virtual returns (bytes32[] memory leaves);

  /**
   * @dev Internal function for fetching validator deposit amount
   */
  function _validatorDeposit() internal pure virtual returns (uint256);

  /**
   * @dev Initializes the VaultValidators contract
   * @dev NB! This initializer must be called after VaultState initializer
   */
  function __VaultValidators_init() internal view onlyInitializing {
    if (capacity() < _validatorDeposit()) revert Errors.InvalidCapacity();
  }

  /**
   * @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;
}
StorageSlot.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     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;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 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) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

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

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

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

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            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) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            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) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}
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
        );
}
SafeCast.sol 1153 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 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);
    }
}
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;
}
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);
}
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 shares to assets
   * @param assets The amount of assets to convert to shares
   * @return shares The amount of shares that the OsToken would exchange for the amount of assets provided
   */
  function convertToShares(uint256 assets) external view returns (uint256 shares);

  /**
   * @notice Converts assets to shares
   * @param shares The amount of shares to convert to assets
   * @return assets The amount of assets that the OsToken would exchange for the amount of 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;
}
draft-IERC1822.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC1822: 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);
}
ERC1967Utils.sol 193 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 */
library ERC1967Utils {
    // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
    // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
    /**
     * @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);

    /**
     * @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 EIP1967 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 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 EIP1967) 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 EIP1967 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 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 EIP1967 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 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();
        }
    }
}
MerkleProof.sol 232 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.20;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Sorts the pair (a, b) and hashes the result.
     */
    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}
Initializable.sol 228 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 reininitialization) 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 Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
UUPSUpgradeable.sol 153 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.20;

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 ERC1967) 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 ERC1167 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 ERC1822 {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 ERC1967-compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    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 ERC1967.
     *
     * 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);
        }
    }
}
ReentrancyGuardUpgradeable.sol 105 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 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;
    }
}

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
getExitQueueIndex 0x60d60e6e → int256
getShares 0xf04da65b → uint256
implementation 0x5c60da1b → address
isStateUpdateRequired 0x72b410a8 → bool
keysManager 0x5dddf3a8 → address
mevEscrow 0x3229fa95 → address
osTokenPositions 0x4ec96b22 → uint128
proxiableUUID 0x52d1902d → bytes32
queuedShares 0xd83ad00c → uint128
totalAssets 0x01e1d114 → uint256
totalShares 0x3a98ef39 → uint256
validatorIndex 0xa1bf49aa → uint256
validatorsRoot 0x9b401cde → bytes32
vaultId 0x33194c0a → bytes32
version 0x54fd4d50 → uint8
whitelistedAccounts 0xf6a6830f → bool
whitelister 0x22758a4a → address
withdrawableAssets 0x2cdf7401 → uint256

Write Contract 22 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
returns: uint256, uint256, uint256
deposit 0xf9609f08
address receiver
address referrer
returns: uint256
enterExitQueue 0x8ceab9aa
uint256 shares
address receiver
returns: uint256
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
redeem 0x7bde82f2
uint256 shares
address receiver
returns: uint256
redeemOsToken 0x43e82a79
uint256 osTokenShares
address owner
address receiver
registerValidator 0x5316a4d5
tuple keeperParams
bytes32[] proof
registerValidators 0x8cfe124e
tuple keeperParams
uint256[] indexes
bool[] proofFlags
bytes32[] proof
setFeeRecipient 0xe74b981b
address _feeRecipient
setKeysManager 0xaaa4e836
address keysManager_
setMetadata 0xa49a1e7d
string metadataIpfsHash
setValidatorsRoot 0x514e2708
bytes32 _validatorsRoot
setWhitelister 0xf98f5b92
address _whitelister
updateState 0x6971af8f
tuple harvestParams
updateStateAndDeposit 0x479476d8
address receiver
address referrer
tuple harvestParams
returns: uint256
updateWhitelist 0x0d392cd9
address account
bool approved
upgradeToAndCall 0x4f1ef286
address newImplementation
bytes data

Recent Transactions

No transactions found for this address