Address Contract Verified
Address
0xB53a6c402b0d4fb6c7AA59B7d8FBD2e884FbF3bC
Balance
0 ETH
Nonce
1
Code Size
19121 bytes
Creator
0x229f53eF...f65d at tx 0x3d0e4c52...b45ebf
Indexed Transactions
0
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