Address Contract Verified
Address
0xd78811bE4d35f40dBe1123FA75fb178b263DF616
Balance
0 ETH
Nonce
1
Code Size
24003 bytes
Creator
0x70d3060A...1312 at tx 0x05455726...62ec04
Indexed Transactions
0
Contract Bytecode
24003 bytes
0x608060405234801561001057600080fd5b50600436106102945760003560e01c80635df45946116101675780639feab859116100ce578063d75b4c8811610087578063d75b4c88146106f5578063dd8283f314610708578063e65797ad1461071b578063f2fde38b146107be578063fabc1cbc146107d1578063fd39105a146107e457600080fd5b80639feab85914610659578063a50857bf14610680578063c391425e14610693578063ca0de882146106b3578063ca4f2d97146106da578063d72d8dd6146106ed57600080fd5b8063871ef04911610120578063871ef049146105cc578063886f1195146105df5780638da5cb5b146105f85780639aa1653d146106005780639b5d177b1461061f5780639e9923c21461063257600080fd5b80635df459461461053d5780636347c9001461056457806368304835146105775780636e3b17db1461059e578063715018a6146105b157806384ca5213146105b957600080fd5b806328f61b311161020b5780635140a548116101c45780635140a548146104c85780635865c60c146104db578063595c6a67146104fb5780635ac86ab7146105035780635b0b829f146105225780635c975abb1461053557600080fd5b806328f61b3114610435578063296bb0641461044857806329d1e0c31461045b5780632cdd1e861461046e5780633998fdd3146104815780633c2a7f4c146104a857600080fd5b806310d67a2f1161025d57806310d67a2f1461034a57806313542a4e1461035d578063136439dd146103865780631478851f146103995780631eb812da146103cc578063249a0c421461041557600080fd5b8062cf2ab51461029957806303fd3492146102ae57806304ec6351146102e1578063054310e61461030c5780630cf4b76714610337575b600080fd5b6102ac6102a73660046148b5565b610820565b005b6102ce6102bc3660046148f6565b60009081526098602052604090205490565b6040519081526020015b60405180910390f35b6102f46102ef366004614921565b610936565b6040516001600160c01b0390911681526020016102d8565b609d5461031f906001600160a01b031681565b6040516001600160a01b0390911681526020016102d8565b6102ac610345366004614a40565b610b2c565b6102ac610358366004614ab5565b610c14565b6102ce61036b366004614ab5565b6001600160a01b031660009081526099602052604090205490565b6102ac6103943660046148f6565b610cc7565b6103bc6103a73660046148f6565b609a6020526000908152604090205460ff1681565b60405190151581526020016102d8565b6103df6103da366004614ad2565b610e04565b60408051825163ffffffff908116825260208085015190911690820152918101516001600160c01b0316908201526060016102d8565b6102ce610423366004614b05565b609b6020526000908152604090205481565b609e5461031f906001600160a01b031681565b61031f6104563660046148f6565b610e95565b6102ac610469366004614ab5565b610f21565b6102ac61047c366004614ab5565b610f32565b61031f7f00000000000000000000000018343aa10e3d2f3a861e5649627324aead987adf81565b6104bb6104b6366004614ab5565b610f43565b6040516102d89190614b20565b6102ac6104d6366004614b78565b610fc2565b6104ee6104e9366004614ab5565b6114d3565b6040516102d89190614c1b565b6102ac611547565b6103bc610511366004614b05565b6001805460ff9092161b9081161490565b6102ac610530366004614ca0565b611613565b6001546102ce565b61031f7f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d4581565b61031f6105723660046148f6565b6116aa565b61031f7f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a1281565b6102ac6105ac366004614cd4565b6116d4565b6102ac611794565b6102ce6105c7366004614d8b565b6117a8565b6102f46105da3660046148f6565b6117f2565b60005461031f906201000090046001600160a01b031681565b61031f6117fd565b60965461060d9060ff1681565b60405160ff90911681526020016102d8565b6102ac61062d366004614f24565b611816565b61031f7f000000000000000000000000c6a464e39d4fa5013d61295501c7ccd050d7661281565b6102ce7f2bd82124057f0913bc3b772ce7b83e8057c1ad1f3510fc83778be20f10ec5de681565b6102ac61068e36600461501d565b611b4e565b6106a66106a13660046150c5565b611cd2565b6040516102d8919061516a565b6102ce7f4d404e3276e7ac2163d8ee476afa6a41d1f68fb71f2d8b6546b24e55ce01b72a81565b6102ac6106e83660046151b4565b611d8b565b609c546102ce565b6102ac61070336600461529a565b611df2565b6102ac61071636600461544d565b611e05565b61078a610729366004614b05565b60408051606080820183526000808352602080840182905292840181905260ff9490941684526097825292829020825193840183525463ffffffff8116845261ffff600160201b8204811692850192909252600160301b9004169082015290565b60408051825163ffffffff16815260208084015161ffff9081169183019190915292820151909216908201526060016102d8565b6102ac6107cc366004614ab5565b612109565b6102ac6107df3660046148f6565b61217f565b6108136107f2366004614ab5565b6001600160a01b031660009081526099602052604090206001015460ff1690565b6040516102d89190615521565b600154600290600490811614156108525760405162461bcd60e51b81526004016108499061552f565b60405180910390fd5b60005b8281101561093057600084848381811061087157610871615566565b90506020020160208101906108869190614ab5565b6001600160a01b03811660009081526099602090815260408083208151808301909252805482526001810154949550929390929183019060ff1660028111156108d1576108d1614be3565b60028111156108e2576108e2614be3565b905250805190915060006108f5826122db565b9050600061090b826001600160c01b0316612344565b9050610918858583612410565b5050505050808061092890615592565b915050610855565b50505050565b600083815260986020526040812080548291908490811061095957610959615566565b600091825260209182902060408051606081018252929091015463ffffffff808216808552600160201b8304821695850195909552600160401b9091046001600160c01b03169183019190915290925085161015610a535760405162461bcd60e51b815260206004820152606560248201527f5265676973747279436f6f7264696e61746f722e67657451756f72756d42697460448201527f6d61704174426c6f636b4e756d6265724279496e6465783a2071756f72756d4260648201527f69746d61705570646174652069732066726f6d20616674657220626c6f636b4e6084820152643ab6b132b960d91b60a482015260c401610849565b602081015163ffffffff161580610a795750806020015163ffffffff168463ffffffff16105b610b205760405162461bcd60e51b815260206004820152606660248201527f5265676973747279436f6f7264696e61746f722e67657451756f72756d42697460448201527f6d61704174426c6f636b4e756d6265724279496e6465783a2071756f72756d4260648201527f69746d61705570646174652069732066726f6d206265666f726520626c6f636b608482015265273ab6b132b960d11b60a482015260c401610849565b60400151949350505050565b60013360009081526099602052604090206001015460ff166002811115610b5557610b55614be3565b14610bc85760405162461bcd60e51b815260206004820152603c60248201527f5265676973747279436f6f7264696e61746f722e757064617465536f636b657460448201527f3a206f70657261746f72206973206e6f742072656769737465726564000000006064820152608401610849565b33600090815260996020526040908190205490517fec2963ab21c1e50e1e582aa542af2e4bf7bf38e6e1403c27b42e1c5d6e621eaa90610c099084906155fa565b60405180910390a250565b600060029054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8b919061560d565b6001600160a01b0316336001600160a01b031614610cbb5760405162461bcd60e51b81526004016108499061562a565b610cc4816124fd565b50565b60005460405163237dfb4760e11b8152336004820152620100009091046001600160a01b0316906346fbf68e90602401602060405180830381865afa158015610d14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d389190615674565b610d545760405162461bcd60e51b815260040161084990615696565b60015481811614610dcd5760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e70617573653a20696e76616c696420617474656d70742060448201527f746f20756e70617573652066756e6374696f6e616c69747900000000000000006064820152608401610849565b600181905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d90602001610c09565b60408051606081018252600080825260208201819052918101919091526000838152609860205260409020805483908110610e4157610e41615566565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160c01b03600160401b909304929092169082015290505b92915050565b6040516308f6629d60e31b8152600481018290526000907f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d456001600160a01b0316906347b314e890602401602060405180830381865afa158015610efd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8f919061560d565b610f29612602565b610cc481612661565b610f3a612602565b610cc4816126ca565b6040805180820190915260008082526020820152610e8f610fbd7f2bd82124057f0913bc3b772ce7b83e8057c1ad1f3510fc83778be20f10ec5de684604051602001610fa29291909182526001600160a01b0316602082015260400190565b60405160208183030381529060405280519060200120612733565b612781565b60015460029060049081161415610feb5760405162461bcd60e51b81526004016108499061552f565b600061103384848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060965460ff1691506128119050565b90508483146110a45760405162461bcd60e51b81526020600482015260436024820152600080516020615d2e83398151915260448201527f6f7273466f7251756f72756d3a20696e707574206c656e677468206d69736d616064820152620e8c6d60eb1b608482015260a401610849565b60005b838110156114ca5760008585838181106110c3576110c3615566565b919091013560f81c915036905060008989858181106110e4576110e4615566565b90506020028101906110f691906156de565b6040516379a0849160e11b815260ff8616600482015291935091507f000000000000000000000000c6a464e39d4fa5013d61295501c7ccd050d766126001600160a01b03169063f341092290602401602060405180830381865afa158015611162573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111869190615727565b63ffffffff1681146112225760405162461bcd60e51b81526020600482015260656024820152600080516020615d2e83398151915260448201527f6f7273466f7251756f72756d3a206e756d626572206f6620757064617465642060648201527f6f70657261746f727320646f6573206e6f74206d617463682071756f72756d206084820152641d1bdd185b60da1b60a482015260c401610849565b6000805b8281101561146957600084848381811061124257611242615566565b90506020020160208101906112579190614ab5565b6001600160a01b03811660009081526099602090815260408083208151808301909252805482526001810154949550929390929183019060ff1660028111156112a2576112a2614be3565b60028111156112b3576112b3614be3565b905250805190915060006112c6826122db565b905060016001600160c01b03821660ff8b161c81161461134a5760405162461bcd60e51b815260206004820152604460248201819052600080516020615d2e833981519152908201527f6f7273466f7251756f72756d3a206f70657261746f72206e6f7420696e2071756064820152636f72756d60e01b608482015260a401610849565b856001600160a01b0316846001600160a01b0316116113f55760405162461bcd60e51b81526020600482015260676024820152600080516020615d2e83398151915260448201527f6f7273466f7251756f72756d3a206f70657261746f7273206172726179206d7560648201527f737420626520736f7274656420696e20617363656e64696e6720616464726573608482015266399037b93232b960c91b60a482015260c401610849565b5061145383838f8f8d908e600161140c9190615744565b926114199392919061575c565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061241092505050565b50909250611462905081615592565b9050611226565b5060ff84166000818152609b6020908152604091829020439081905591519182527f46077d55330763f16269fd75e5761663f4192d2791747c0189b16ad31db07db4910160405180910390a250505050806114c390615592565b90506110a7565b50505050505050565b60408051808201909152600080825260208201526001600160a01b0382166000908152609960209081526040918290208251808401909352805483526001810154909183019060ff16600281111561152d5761152d614be3565b600281111561153e5761153e614be3565b90525092915050565b60005460405163237dfb4760e11b8152336004820152620100009091046001600160a01b0316906346fbf68e90602401602060405180830381865afa158015611594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b89190615674565b6115d45760405162461bcd60e51b815260040161084990615696565b600019600181905560405190815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2565b61161b612602565b609654829060ff9081169082161061169b5760405162461bcd60e51b815260206004820152603760248201527f5265676973747279436f6f7264696e61746f722e71756f72756d45786973747360448201527f3a2071756f72756d20646f6573206e6f742065786973740000000000000000006064820152608401610849565b6116a583836128a2565b505050565b609c81815481106116ba57600080fd5b6000918252602090912001546001600160a01b0316905081565b609e546001600160a01b031633146117545760405162461bcd60e51b815260206004820152603a60248201527f5265676973747279436f6f7264696e61746f722e6f6e6c79456a6563746f723a60448201527f2063616c6c6572206973206e6f742074686520656a6563746f720000000000006064820152608401610849565b6116a58383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061294f92505050565b61179c612602565b6117a66000612dc1565b565b60006117e87f4d404e3276e7ac2163d8ee476afa6a41d1f68fb71f2d8b6546b24e55ce01b72a8787878787604051602001610fa296959493929190615786565b9695505050505050565b6000610e8f826122db565b60006118116064546001600160a01b031690565b905090565b60018054600091908116141561183e5760405162461bcd60e51b81526004016108499061552f565b8389146118c15760405162461bcd60e51b8152602060048201526044602482018190527f5265676973747279436f6f7264696e61746f722e72656769737465724f706572908201527f61746f7257697468436875726e3a20696e707574206c656e677468206d69736d6064820152630c2e8c6d60e31b608482015260a401610849565b60006118cd3388612e13565b905061192d33828888808060200260200160405190810160405280939291908181526020016000905b82821015611922576119136040830286013681900381019061580b565b815260200190600101906118f6565b505050505087612f44565b600061197433838e8e8e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92506130d1915050565b905060005b8b811015611b3f576000609760008f8f8581811061199957611999615566565b919091013560f81c82525060208082019290925260409081016000208151606081018352905463ffffffff811680835261ffff600160201b8304811695840195909552600160301b90910490931691810191909152845180519193509084908110611a0657611a06615566565b602002602001015163ffffffff161115611b2c57611aa78e8e84818110611a2f57611a2f615566565b9050013560f81c60f81b60f81c84604001518481518110611a5257611a52615566565b60200260200101513386602001518681518110611a7157611a71615566565b60200260200101518d8d88818110611a8b57611a8b615566565b905060400201803603810190611aa1919061580b565b866135e8565b611b2c898984818110611abc57611abc615566565b9050604002016020016020810190611ad49190614ab5565b8f8f8590866001611ae59190615744565b92611af29392919061575c565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061294f92505050565b5080611b3781615592565b915050611979565b50505050505050505050505050565b600180546000919081161415611b765760405162461bcd60e51b81526004016108499061552f565b6000611b823385612e13565b90506000611bcb33838b8b8b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92506130d1915050565b51905060005b88811015611cc65760008a8a83818110611bed57611bed615566565b919091013560f81c600081815260976020526040902054855191935063ffffffff169150849084908110611c2357611c23615566565b602002602001015163ffffffff161115611cb35760405162461bcd60e51b8152602060048201526044602482018190527f5265676973747279436f6f7264696e61746f722e72656769737465724f706572908201527f61746f723a206f70657261746f7220636f756e742065786365656473206d6178606482015263696d756d60e01b608482015260a401610849565b5080611cbe81615592565b915050611bd1565b50505050505050505050565b6060600082516001600160401b03811115611cef57611cef614959565b604051908082528060200260200182016040528015611d18578160200160208202803683370190505b50905060005b8351811015611d8357611d4a85858381518110611d3d57611d3d615566565b60200260200101516138bd565b828281518110611d5c57611d5c615566565b63ffffffff9092166020928302919091019091015280611d7b81615592565b915050611d1e565b509392505050565b6001805460029081161415611db25760405162461bcd60e51b81526004016108499061552f565b6116a53384848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061294f92505050565b611dfa612602565b6116a58383836139f9565b600054610100900460ff1615808015611e255750600054600160ff909116105b80611e3f5750303b158015611e3f575060005460ff166001145b611ea25760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610849565b6000805460ff191660011790558015611ec5576000805461ff0019166101001790555b82518451148015611ed7575081518351145b611f415760405162461bcd60e51b815260206004820152603560248201527f5265676973747279436f6f7264696e61746f722e696e697469616c697a653a206044820152740d2dce0eae840d8cadccee8d040dad2e6dac2e8c6d605b1b6064820152608401610849565b611f4a89612dc1565b611f548686613c10565b611f5d88612661565b611f66876126ca565b609c80546001818101835560008381527faf85b9071dfafeac1409d3f1d19bafc9bc7c37974cde8df0ee6168f0086e539c92830180546001600160a01b037f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a1281166001600160a01b03199283161790925585548085018755850180547f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d45841690831617905585549384019095559190920180547f000000000000000000000000c6a464e39d4fa5013d61295501c7ccd050d7661290921691909316179091555b84518110156120b7576120a585828151811061206457612064615566565b602002602001015185838151811061207e5761207e615566565b602002602001015185848151811061209857612098615566565b60200260200101516139f9565b806120af81615592565b915050612046565b5080156120fe576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050505050565b612111612602565b6001600160a01b0381166121765760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610849565b610cc481612dc1565b600060029054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f6919061560d565b6001600160a01b0316336001600160a01b0316146122265760405162461bcd60e51b81526004016108499061562a565b6001541981196001541916146122a45760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e756e70617573653a20696e76616c696420617474656d7060448201527f7420746f2070617573652066756e6374696f6e616c69747900000000000000006064820152608401610849565b600181905560405181815233907f3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c90602001610c09565b600081815260986020526040812054806122f85750600092915050565b6000838152609860205260409020612311600183615827565b8154811061232157612321615566565b600091825260209091200154600160401b90046001600160c01b03169392505050565b606060008061235284613d00565b61ffff166001600160401b0381111561236d5761236d614959565b6040519080825280601f01601f191660200182016040528015612397576020820181803683370190505b5090506000805b8251821080156123af575061010081105b15612406576001811b9350858416156123f6578060f81b8383815181106123d8576123d8615566565b60200101906001600160f81b031916908160001a9053508160010191505b6123ff81615592565b905061239e565b5090949350505050565b60018260200151600281111561242857612428614be3565b1461243257505050565b81516040516333567f7f60e11b81526000906001600160a01b037f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a1216906366acfefe906124879088908690889060040161583e565b6020604051808303816000875af11580156124a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ca919061586e565b90506001600160c01b038116156124f6576124f6856124f1836001600160c01b0316612344565b61294f565b5050505050565b6001600160a01b03811661258b5760405162461bcd60e51b815260206004820152604960248201527f5061757361626c652e5f73657450617573657252656769737472793a206e657760448201527f50617573657252656769737472792063616e6e6f7420626520746865207a65726064820152686f206164647265737360b81b608482015260a401610849565b600054604080516001600160a01b03620100009093048316815291831660208301527f6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6910160405180910390a1600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b3361260b6117fd565b6001600160a01b0316146117a65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610849565b609d54604080516001600160a01b03928316815291831660208301527f315457d8a8fe60f04af17c16e2f5a5e1db612b31648e58030360759ef8f3528c910160405180910390a1609d80546001600160a01b0319166001600160a01b0392909216919091179055565b609e54604080516001600160a01b03928316815291831660208301527f8f30ab09f43a6c157d7fce7e0a13c003042c1c95e8a72e7a146a21c0caa24dc9910160405180910390a1609e80546001600160a01b0319166001600160a01b0392909216919091179055565b6000610e8f612740613d2b565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b6040805180820190915260008082526020820152600080806127b1600080516020615d6e833981519152866158ad565b90505b6127bd81613e52565b9093509150600080516020615d6e8339815191528283098314156127f7576040805180820190915290815260208101919091529392505050565b600080516020615d6e8339815191526001820890506127b4565b60008061281d84613ed4565b9050808360ff166001901b1161289b5760405162461bcd60e51b815260206004820152603f60248201527f4269746d61705574696c732e6f72646572656442797465734172726179546f4260448201527f69746d61703a206269746d61702065786365656473206d61782076616c7565006064820152608401610849565b9392505050565b60ff8216600081815260976020908152604091829020845181548684018051888701805163ffffffff90951665ffffffffffff199094168417600160201b61ffff938416021767ffff0000000000001916600160301b95831695909502949094179094558551918252518316938101939093525116918101919091527f3ee6fe8d54610244c3e9d3c066ae4aee997884aa28f10616ae821925401318ac9060600160405180910390a25050565b6001600160a01b0382166000908152609960205260409020805460018083015460ff16600281111561298357612983614be3565b14612a025760405162461bcd60e51b815260206004820152604360248201527f5265676973747279436f6f7264696e61746f722e5f646572656769737465724f60448201527f70657261746f723a206f70657261746f72206973206e6f7420726567697374656064820152621c995960ea1b608482015260a401610849565b609654600090612a1690859060ff16612811565b90506000612a23836122db565b90506001600160c01b038216612aa15760405162461bcd60e51b815260206004820152603b60248201527f5265676973747279436f6f7264696e61746f722e5f646572656769737465724f60448201527f70657261746f723a206269746d61702063616e6e6f74206265203000000000006064820152608401610849565b612ab86001600160c01b0383811690831681161490565b612b505760405162461bcd60e51b815260206004820152605960248201527f5265676973747279436f6f7264696e61746f722e5f646572656769737465724f60448201527f70657261746f723a206f70657261746f72206973206e6f74207265676973746560648201527f72656420666f72207370656369666965642071756f72756d7300000000000000608482015260a401610849565b6001600160c01b0382811619821616612b698482614061565b6001600160c01b038116612c385760018501805460ff191660021790556040516351b27a6d60e11b81526001600160a01b0388811660048301527f00000000000000000000000018343aa10e3d2f3a861e5649627324aead987adf169063a364f4da90602401600060405180830381600087803b158015612be957600080fd5b505af1158015612bfd573d6000803e3d6000fd5b50506040518692506001600160a01b038a1691507f396fdcb180cb0fea26928113fb0fd1c3549863f9cd563e6a184f1d578116c8e490600090a35b60405163f4e24fe560e01b81526001600160a01b037f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d45169063f4e24fe590612c86908a908a906004016158c1565b600060405180830381600087803b158015612ca057600080fd5b505af1158015612cb4573d6000803e3d6000fd5b505060405163bd29b8cd60e01b81526001600160a01b037f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a1216925063bd29b8cd9150612d069087908a906004016158e5565b600060405180830381600087803b158015612d2057600080fd5b505af1158015612d34573d6000803e3d6000fd5b505060405163bd29b8cd60e01b81526001600160a01b037f000000000000000000000000c6a464e39d4fa5013d61295501c7ccd050d7661216925063bd29b8cd9150612d869087908a906004016158e5565b600060405180830381600087803b158015612da057600080fd5b505af1158015612db4573d6000803e3d6000fd5b5050505050505050505050565b606480546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6040516309aa152760e11b81526001600160a01b0383811660048301526000917f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d45909116906313542a4e90602401602060405180830381865afa158015612e7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea291906158fe565b905080610e8f577f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d456001600160a01b031663bf79ce588484612ee387610f43565b6040518463ffffffff1660e01b8152600401612f0193929190615917565b6020604051808303816000875af1158015612f20573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061289b91906158fe565b6020808201516000908152609a909152604090205460ff1615612fea5760405162461bcd60e51b815260206004820152605260248201527f5265676973747279436f6f7264696e61746f722e5f766572696679436875726e60448201527f417070726f7665725369676e61747572653a20636875726e417070726f766572606482015271081cd85b1d08185b1c9958591e481d5cd95960721b608482015260a401610849565b428160400151101561307f5760405162461bcd60e51b815260206004820152605260248201527f5265676973747279436f6f7264696e61746f722e5f766572696679436875726e60448201527f417070726f7665725369676e61747572653a20636875726e417070726f766572606482015271081cda59db985d1d5c9948195e1c1a5c995960721b608482015260a401610849565b602080820180516000908152609a909252604091829020805460ff19166001179055609d54905191830151610930926001600160a01b03909216916130ca91889188918891906117a8565b8351614221565b6130f560405180606001604052806060815260200160608152602001606081525090565b600061313d86868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060965460ff1691506128119050565b9050600061314a886122db565b90506001600160c01b0382166131c85760405162461bcd60e51b815260206004820152603960248201527f5265676973747279436f6f7264696e61746f722e5f72656769737465724f706560448201527f7261746f723a206269746d61702063616e6e6f742062652030000000000000006064820152608401610849565b8082166001600160c01b03161561327e5760405162461bcd60e51b815260206004820152606860248201527f5265676973747279436f6f7264696e61746f722e5f72656769737465724f706560448201527f7261746f723a206f70657261746f7220616c726561647920726567697374657260648201527f656420666f7220736f6d652071756f72756d73206265696e672072656769737460848201526732b932b2103337b960c11b60a482015260c401610849565b6001600160c01b03818116908316176132978982614061565b887fec2963ab21c1e50e1e582aa542af2e4bf7bf38e6e1403c27b42e1c5d6e621eaa876040516132c791906155fa565b60405180910390a260016001600160a01b038b1660009081526099602052604090206001015460ff16600281111561330157613301614be3565b1461341a576040805180820182528a8152600160208083018281526001600160a01b038f166000908152609990925293902082518155925183820180549394939192909160ff19169083600281111561335c5761335c614be3565b021790555050604051639926ee7d60e01b81526001600160a01b037f00000000000000000000000018343aa10e3d2f3a861e5649627324aead987adf169150639926ee7d906133b1908d908990600401615996565b600060405180830381600087803b1580156133cb57600080fd5b505af11580156133df573d6000803e3d6000fd5b50506040518b92506001600160a01b038d1691507fe8e68cef1c3a761ed7be7e8463a375f27f7bc335e51824223cacce636ec5c3fe90600090a35b604051631fd93ca960e11b81526001600160a01b037f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d451690633fb279529061346a908d908c908c90600401615a0a565b600060405180830381600087803b15801561348457600080fd5b505af1158015613498573d6000803e3d6000fd5b5050604051632550477760e01b81526001600160a01b037f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a12169250632550477791506134ee908d908d908d908d90600401615a2f565b6000604051808303816000875af115801561350d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526135359190810190615abb565b60408087019190915260208601919091525162bff04d60e01b81526001600160a01b037f000000000000000000000000c6a464e39d4fa5013d61295501c7ccd050d76612169062bff04d90613592908c908c908c90600401615b1e565b6000604051808303816000875af11580156135b1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526135d99190810190615b38565b84525050509695505050505050565b6020808301516001600160a01b0380821660008181526099909452604090932054919290871614156136685760405162461bcd60e51b81526020600482015260356024820152600080516020615d4e83398151915260448201527439371d1031b0b73737ba1031b43ab9371039b2b63360591b6064820152608401610849565b8760ff16846000015160ff16146136e55760405162461bcd60e51b81526020600482015260476024820152600080516020615d4e83398151915260448201527f726e3a2071756f72756d4e756d626572206e6f74207468652073616d65206173606482015266081cda59db995960ca1b608482015260a401610849565b604051635401ed2760e01b81526004810182905260ff891660248201526000907f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a126001600160a01b031690635401ed2790604401602060405180830381865afa158015613756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061377a9190615bd1565b905061378681856143db565b6001600160601b0316866001600160601b0316116138195760405162461bcd60e51b81526020600482015260566024820152600080516020615d4e83398151915260448201527f726e3a20696e636f6d696e67206f70657261746f722068617320696e7375666660648201527534b1b4b2b73a1039ba30b5b2903337b91031b43ab93760511b608482015260a401610849565b61382388856143ff565b6001600160601b0316816001600160601b0316106120fe5760405162461bcd60e51b815260206004820152605c6024820152600080516020615d4e83398151915260448201527f726e3a2063616e6e6f74206b69636b206f70657261746f722077697468206d6f60648201527f7265207468616e206b69636b424950734f66546f74616c5374616b6500000000608482015260a401610849565b600081815260986020526040812054815b8181101561394f5760016138e28284615827565b6138ec9190615827565b92508463ffffffff16609860008681526020019081526020016000208463ffffffff168154811061391f5761391f615566565b60009182526020909120015463ffffffff161161393d575050610e8f565b8061394781615592565b9150506138ce565b5060405162461bcd60e51b815260206004820152606c60248201527f5265676973747279436f6f7264696e61746f722e67657451756f72756d42697460448201527f6d6170496e6465784174426c6f636b4e756d6265723a206e6f206269746d617060648201527f2075706461746520666f756e6420666f72206f70657261746f7249642061742060848201526b313637b1b590373ab6b132b960a11b60a482015260c401610849565b60965460ff1660c08110613a6d5760405162461bcd60e51b815260206004820152603560248201527f5265676973747279436f6f7264696e61746f722e63726561746551756f72756d6044820152740e881b585e081c5d5bdc9d5b5cc81c995858da1959605a1b6064820152608401610849565b613a78816001615bee565b6096805460ff191660ff9290921691909117905580613a9781866128a2565b60405160016296b58960e01b031981526001600160a01b037f000000000000000000000000363b3604fe8c2323a98c00906115c8b87a512a12169063ff694a7790613aea90849088908890600401615c13565b600060405180830381600087803b158015613b0457600080fd5b505af1158015613b18573d6000803e3d6000fd5b505060405163136ca0f960e11b815260ff841660048201527f000000000000000000000000c6a464e39d4fa5013d61295501c7ccd050d766126001600160a01b031692506326d941f29150602401600060405180830381600087803b158015613b8057600080fd5b505af1158015613b94573d6000803e3d6000fd5b505060405163136ca0f960e11b815260ff841660048201527f000000000000000000000000b58687ff303c8e92c28a484342755d3228081d456001600160a01b031692506326d941f29150602401600060405180830381600087803b158015613bfc57600080fd5b505af11580156120fe573d6000803e3d6000fd5b6000546201000090046001600160a01b0316158015613c3757506001600160a01b03821615155b613cb95760405162461bcd60e51b815260206004820152604760248201527f5061757361626c652e5f696e697469616c697a655061757365723a205f696e6960448201527f7469616c697a6550617573657228292063616e206f6e6c792062652063616c6c6064820152666564206f6e636560c81b608482015260a401610849565b600181905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2613cfc826124fd565b5050565b6000805b8215610e8f57613d15600184615827565b9092169180613d2381615c8c565b915050613d04565b6000306001600160a01b037f000000000000000000000000d78811be4d35f40dbe1123fa75fb178b263df61616148015613d8457507f000000000000000000000000000000000000000000000000000000000000000146145b15613dae57507ff8961c653d86f8436638b259e380ed3ab35cf0f747c3baea0aad9c359fbda49090565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527f6ec8a99f0e7f9ebde7354a446dcb9423f3af9c58f386a53c59c5b384f9e82d11828401527f6bda7e3f385e48841048390444cced5cc795af87758af67622e5f4f0882c4a9960608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b60008080600080516020615d6e8339815191526003600080516020615d6e83398151915286600080516020615d6e833981519152888909090890506000613ec8827f0c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52600080516020615d6e833981519152614419565b91959194509092505050565b600061010082511115613f5d5760405162461bcd60e51b8152602060048201526044602482018190527f4269746d61705574696c732e6f72646572656442797465734172726179546f42908201527f69746d61703a206f7264657265644279746573417272617920697320746f6f206064820152636c6f6e6760e01b608482015260a401610849565b8151613f6b57506000919050565b60008083600081518110613f8157613f81615566565b0160200151600160f89190911c81901b92505b845181101561405857848181518110613faf57613faf615566565b0160200151600160f89190911c1b91508282116140445760405162461bcd60e51b815260206004820152604760248201527f4269746d61705574696c732e6f72646572656442797465734172726179546f4260448201527f69746d61703a206f72646572656442797465734172726179206973206e6f74206064820152661bdc99195c995960ca1b608482015260a401610849565b9181179161405181615592565b9050613f94565b50909392505050565b60008281526098602052604090205480614106576000838152609860209081526040808320815160608101835263ffffffff43811682528185018681526001600160c01b03808a16958401958652845460018101865594885295909620915191909201805495519351909416600160401b026001600160401b03938316600160201b0267ffffffffffffffff1990961691909216179390931716919091179055505050565b600083815260986020526040812061411f600184615827565b8154811061412f5761412f615566565b600091825260209091200180549091504363ffffffff908116911614156141735780546001600160401b0316600160401b6001600160c01b03851602178155610930565b805463ffffffff438116600160201b81810267ffffffff0000000019909416939093178455600087815260986020908152604080832081516060810183529485528483018481526001600160c01b03808c1693870193845282546001810184559286529390942094519401805493519151909216600160401b026001600160401b0391861690960267ffffffffffffffff199093169390941692909217179190911691909117905550505050565b6001600160a01b0383163b1561433b57604051630b135d3f60e11b808252906001600160a01b03851690631626ba7e9061426190869086906004016158e5565b602060405180830381865afa15801561427e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a29190615cae565b6001600160e01b031916146116a55760405162461bcd60e51b815260206004820152605360248201527f454950313237315369676e61747572655574696c732e636865636b5369676e6160448201527f747572655f454950313237313a2045524331323731207369676e6174757265206064820152721d995c9a599a58d85d1a5bdb8819985a5b1959606a1b608482015260a401610849565b826001600160a01b031661434f83836144c8565b6001600160a01b0316146116a55760405162461bcd60e51b815260206004820152604760248201527f454950313237315369676e61747572655574696c732e636865636b5369676e6160448201527f747572655f454950313237313a207369676e6174757265206e6f742066726f6d6064820152661039b4b3b732b960c91b608482015260a401610849565b6020810151600090612710906143f59061ffff1685615cd8565b61289b9190615d07565b6040810151600090612710906143f59061ffff1685615cd8565b600080614424614835565b61442c614853565b602080825281810181905260408201819052606082018890526080820187905260a082018690528260c08360056107d05a03fa925082801561446d5761446f565bfe5b50826144bd5760405162461bcd60e51b815260206004820152601a60248201527f424e3235342e6578704d6f643a2063616c6c206661696c7572650000000000006044820152606401610849565b505195945050505050565b60008060006144d785856144e4565b91509150611d8381614554565b60008082516041141561451b5760208301516040840151606085015160001a61450f8782858561470f565b9450945050505061454d565b825160401415614545576020830151604084015161453a8683836147fc565b93509350505061454d565b506000905060025b9250929050565b600081600481111561456857614568614be3565b14156145715750565b600181600481111561458557614585614be3565b14156145d35760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610849565b60028160048111156145e7576145e7614be3565b14156146355760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610849565b600381600481111561464957614649614be3565b14156146a25760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610849565b60048160048111156146b6576146b6614be3565b1415610cc45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610849565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561474657506000905060036147f3565b8460ff16601b1415801561475e57508460ff16601c14155b1561476f57506000905060046147f3565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147c3573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166147ec576000600192509250506147f3565b9150600090505b94509492505050565b6000806001600160ff1b0383168161481960ff86901c601b615744565b90506148278782888561470f565b935093505050935093915050565b60405180602001604052806001906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60008083601f84011261488357600080fd5b5081356001600160401b0381111561489a57600080fd5b6020830191508360208260051b850101111561454d57600080fd5b600080602083850312156148c857600080fd5b82356001600160401b038111156148de57600080fd5b6148ea85828601614871565b90969095509350505050565b60006020828403121561490857600080fd5b5035919050565b63ffffffff81168114610cc457600080fd5b60008060006060848603121561493657600080fd5b8335925060208401356149488161490f565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561499157614991614959565b60405290565b604080519081016001600160401b038111828210171561499157614991614959565b604051601f8201601f191681016001600160401b03811182821017156149e1576149e1614959565b604052919050565b60006001600160401b03831115614a0257614a02614959565b614a15601f8401601f19166020016149b9565b9050828152838383011115614a2957600080fd5b828260208301376000602084830101529392505050565b600060208284031215614a5257600080fd5b81356001600160401b03811115614a6857600080fd5b8201601f81018413614a7957600080fd5b614a88848235602084016149e9565b949350505050565b6001600160a01b0381168114610cc457600080fd5b8035614ab081614a90565b919050565b600060208284031215614ac757600080fd5b813561289b81614a90565b60008060408385031215614ae557600080fd5b50508035926020909101359150565b803560ff81168114614ab057600080fd5b600060208284031215614b1757600080fd5b61289b82614af4565b815181526020808301519082015260408101610e8f565b60008083601f840112614b4957600080fd5b5081356001600160401b03811115614b6057600080fd5b60208301915083602082850101111561454d57600080fd5b60008060008060408587031215614b8e57600080fd5b84356001600160401b0380821115614ba557600080fd5b614bb188838901614871565b90965094506020870135915080821115614bca57600080fd5b50614bd787828801614b37565b95989497509550505050565b634e487b7160e01b600052602160045260246000fd5b60038110614c1757634e487b7160e01b600052602160045260246000fd5b9052565b815181526020808301516040830191614c3690840182614bf9565b5092915050565b803561ffff81168114614ab057600080fd5b600060608284031215614c6157600080fd5b614c6961496f565b90508135614c768161490f565b8152614c8460208301614c3d565b6020820152614c9560408301614c3d565b604082015292915050565b60008060808385031215614cb357600080fd5b614cbc83614af4565b9150614ccb8460208501614c4f565b90509250929050565b600080600060408486031215614ce957600080fd5b8335614cf481614a90565b925060208401356001600160401b03811115614d0f57600080fd5b614d1b86828701614b37565b9497909650939450505050565b60006001600160401b03821115614d4157614d41614959565b5060051b60200190565b600060408284031215614d5d57600080fd5b614d65614997565b9050614d7082614af4565b81526020820135614d8081614a90565b602082015292915050565b600080600080600060a08688031215614da357600080fd5b8535614dae81614a90565b945060208681013594506040808801356001600160401b03811115614dd257600080fd5b8801601f81018a13614de357600080fd5b8035614df6614df182614d28565b6149b9565b81815260069190911b8201840190848101908c831115614e1557600080fd5b928501925b82841015614e3b57614e2c8d85614d4b565b82529284019290850190614e1a565b999c989b5098996060810135995060800135979650505050505050565b60006101008284031215614e6b57600080fd5b50919050565b60008083601f840112614e8357600080fd5b5081356001600160401b03811115614e9a57600080fd5b6020830191508360208260061b850101111561454d57600080fd5b600060608284031215614ec757600080fd5b614ecf61496f565b905081356001600160401b03811115614ee757600080fd5b8201601f81018413614ef857600080fd5b614f07848235602084016149e9565b825250602082013560208201526040820135604082015292915050565b60008060008060008060008060006101a08a8c031215614f4357600080fd5b89356001600160401b0380821115614f5a57600080fd5b614f668d838e01614b37565b909b50995060208c0135915080821115614f7f57600080fd5b614f8b8d838e01614b37565b9099509750879150614fa08d60408e01614e58565b96506101408c0135915080821115614fb757600080fd5b614fc38d838e01614e71565b90965094506101608c0135915080821115614fdd57600080fd5b614fe98d838e01614eb5565b93506101808c013591508082111561500057600080fd5b5061500d8c828d01614eb5565b9150509295985092959850929598565b600080600080600080610160878903121561503757600080fd5b86356001600160401b038082111561504e57600080fd5b61505a8a838b01614b37565b9098509650602089013591508082111561507357600080fd5b61507f8a838b01614b37565b90965094508491506150948a60408b01614e58565b93506101408901359150808211156150ab57600080fd5b506150b889828a01614eb5565b9150509295509295509295565b600080604083850312156150d857600080fd5b82356150e38161490f565b91506020838101356001600160401b038111156150ff57600080fd5b8401601f8101861361511057600080fd5b803561511e614df182614d28565b81815260059190911b8201830190838101908883111561513d57600080fd5b928401925b8284101561515b57833582529284019290840190615142565b80955050505050509250929050565b6020808252825182820181905260009190848201906040850190845b818110156151a857835163ffffffff1683529284019291840191600101615186565b50909695505050505050565b600080602083850312156151c757600080fd5b82356001600160401b038111156151dd57600080fd5b6148ea85828601614b37565b6001600160601b0381168114610cc457600080fd5b600082601f83011261520f57600080fd5b8135602061521f614df183614d28565b82815260069290921b8401810191818101908684111561523e57600080fd5b8286015b8481101561528f576040818903121561525b5760008081fd5b615263614997565b813561526e81614a90565b81528185013561527d816151e9565b81860152835291830191604001615242565b509695505050505050565b600080600060a084860312156152af57600080fd5b6152b98585614c4f565b925060608401356152c9816151e9565b915060808401356001600160401b038111156152e457600080fd5b6152f0868287016151fe565b9150509250925092565b600082601f83011261530b57600080fd5b8135602061531b614df183614d28565b8281526060928302850182019282820191908785111561533a57600080fd5b8387015b8581101561535d576153508982614c4f565b845292840192810161533e565b5090979650505050505050565b600082601f83011261537b57600080fd5b8135602061538b614df183614d28565b82815260059290921b840181019181810190868411156153aa57600080fd5b8286015b8481101561528f5780356153c1816151e9565b83529183019183016153ae565b600082601f8301126153df57600080fd5b813560206153ef614df183614d28565b82815260059290921b8401810191818101908684111561540e57600080fd5b8286015b8481101561528f5780356001600160401b038111156154315760008081fd5b61543f8986838b01016151fe565b845250918301918301615412565b600080600080600080600080610100898b03121561546a57600080fd5b61547389614aa5565b975061548160208a01614aa5565b965061548f60408a01614aa5565b955061549d60608a01614aa5565b94506080890135935060a08901356001600160401b03808211156154c057600080fd5b6154cc8c838d016152fa565b945060c08b01359150808211156154e257600080fd5b6154ee8c838d0161536a565b935060e08b013591508082111561550457600080fd5b506155118b828c016153ce565b9150509295985092959890939650565b60208101610e8f8284614bf9565b60208082526019908201527f5061757361626c653a20696e6465782069732070617573656400000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006000198214156155a6576155a661557c565b5060010190565b6000815180845260005b818110156155d3576020818501810151868301820152016155b7565b818111156155e5576000602083870101525b50601f01601f19169290920160200192915050565b60208152600061289b60208301846155ad565b60006020828403121561561f57600080fd5b815161289b81614a90565b6020808252602a908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526939903ab73830bab9b2b960b11b606082015260800190565b60006020828403121561568657600080fd5b8151801515811461289b57600080fd5b60208082526028908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526739903830bab9b2b960c11b606082015260800190565b6000808335601e198436030181126156f557600080fd5b8301803591506001600160401b0382111561570f57600080fd5b6020019150600581901b360382131561454d57600080fd5b60006020828403121561573957600080fd5b815161289b8161490f565b600082198211156157575761575761557c565b500190565b6000808585111561576c57600080fd5b8386111561577957600080fd5b5050820193919092039150565b600060c08201888352602060018060a01b03808a16828601526040898187015260c0606087015283895180865260e088019150848b01955060005b818110156157eb578651805160ff16845286015185168684015295850195918301916001016157c1565b505060808701989098525050505060a09091019190915250949350505050565b60006040828403121561581d57600080fd5b61289b8383614d4b565b6000828210156158395761583961557c565b500390565b60018060a01b038416815282602082015260606040820152600061586560608301846155ad565b95945050505050565b60006020828403121561588057600080fd5b81516001600160c01b038116811461289b57600080fd5b634e487b7160e01b600052601260045260246000fd5b6000826158bc576158bc615897565b500690565b6001600160a01b0383168152604060208201819052600090614a88908301846155ad565b828152604060208201526000614a8860408301846155ad565b60006020828403121561591057600080fd5b5051919050565b6001600160a01b0384168152610160810161593f602083018580358252602090810135910152565b615959606083016040860180358252602090810135910152565b60406080850160a084013760e0820160008152604060c0860182375060006101208301908152835190526020909201516101409091015292915050565b60018060a01b03831681526040602082015260008251606060408401526159c060a08401826155ad565b90506020840151606084015260408401516080840152809150509392505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038416815260406020820181905260009061586590830184866159e1565b60018060a01b03851681528360208201526060604082015260006117e86060830184866159e1565b600082601f830112615a6857600080fd5b81516020615a78614df183614d28565b82815260059290921b84018101918181019086841115615a9757600080fd5b8286015b8481101561528f578051615aae816151e9565b8352918301918301615a9b565b60008060408385031215615ace57600080fd5b82516001600160401b0380821115615ae557600080fd5b615af186838701615a57565b93506020850151915080821115615b0757600080fd5b50615b1485828601615a57565b9150509250929050565b8381526040602082015260006158656040830184866159e1565b60006020808385031215615b4b57600080fd5b82516001600160401b03811115615b6157600080fd5b8301601f81018513615b7257600080fd5b8051615b80614df182614d28565b81815260059190911b82018301908381019087831115615b9f57600080fd5b928401925b82841015615bc6578351615bb78161490f565b82529284019290840190615ba4565b979650505050505050565b600060208284031215615be357600080fd5b815161289b816151e9565b600060ff821660ff84168060ff03821115615c0b57615c0b61557c565b019392505050565b60006060820160ff8616835260206001600160601b03808716828601526040606081870152838751808652608088019150848901955060005b81811015615c7c57865180516001600160a01b031684528601518516868401529585019591830191600101615c4c565b50909a9950505050505050505050565b600061ffff80831681811415615ca457615ca461557c565b6001019392505050565b600060208284031215615cc057600080fd5b81516001600160e01b03198116811461289b57600080fd5b60006001600160601b0380831681851681830481118215151615615cfe57615cfe61557c565b02949350505050565b60006001600160601b0380841680615d2157615d21615897565b9216919091049291505056fe5265676973747279436f6f7264696e61746f722e7570646174654f70657261745265676973747279436f6f7264696e61746f722e5f76616c696461746543687530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47a26469706673582212201cedc35177e701fc9d1ec0bcc00864a4289adf594aa164b04bf79c6e8101976c64736f6c634300080c0033
Verified Source Code Full Match
Compiler: v0.8.12+commit.f00d7308
EVM: london
Optimization: Yes (200 runs)
RegistryCoordinator.sol 919 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
import {BN254} from "./libraries/BN254.sol";
import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol";
import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol";
/**
* @title A `RegistryCoordinator` that has three registries:
* 1) a `StakeRegistry` that keeps track of operators' stakes
* 2) a `BLSApkRegistry` that keeps track of operators' BLS public keys and aggregate BLS public keys for each quorum
* 3) an `IndexRegistry` that keeps track of an ordered list of operators for each quorum
*
* @author Layr Labs, Inc.
*/
contract RegistryCoordinator is
EIP712,
Initializable,
Pausable,
OwnableUpgradeable,
RegistryCoordinatorStorage,
ISocketUpdater,
ISignatureUtils
{
using BitmapUtils for *;
using BN254 for BN254.G1Point;
modifier onlyEjector {
require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector");
_;
}
/// @dev Checks that `quorumNumber` corresponds to a quorum that has been created
/// via `initialize` or `createQuorum`
modifier quorumExists(uint8 quorumNumber) {
require(
quorumNumber < quorumCount,
"RegistryCoordinator.quorumExists: quorum does not exist"
);
_;
}
constructor(
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
)
RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry)
EIP712("AVSRegistryCoordinator", "v0.0.1")
{
_disableInitializers();
}
/**
* @param _initialOwner will hold the owner role
* @param _churnApprover will hold the churnApprover role, which authorizes registering with churn
* @param _ejector will hold the ejector role, which can force-eject operators from quorums
* @param _pauserRegistry a registry of addresses that can pause the contract
* @param _initialPausedStatus pause status after calling initialize
* Config for initial quorums (see `createQuorum`):
* @param _operatorSetParams max operator count and operator churn parameters
* @param _minimumStakes minimum stake weight to allow an operator to register
* @param _strategyParams which Strategies/multipliers a quorum considers when calculating stake weight
*/
function initialize(
address _initialOwner,
address _churnApprover,
address _ejector,
IPauserRegistry _pauserRegistry,
uint256 _initialPausedStatus,
OperatorSetParam[] memory _operatorSetParams,
uint96[] memory _minimumStakes,
IStakeRegistry.StrategyParams[][] memory _strategyParams
) external initializer {
require(
_operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length,
"RegistryCoordinator.initialize: input length mismatch"
);
// Initialize roles
_transferOwnership(_initialOwner);
_initializePauser(_pauserRegistry, _initialPausedStatus);
_setChurnApprover(_churnApprover);
_setEjector(_ejector);
// Add registry contracts to the registries array
registries.push(address(stakeRegistry));
registries.push(address(blsApkRegistry));
registries.push(address(indexRegistry));
// Create quorums
for (uint256 i = 0; i < _operatorSetParams.length; i++) {
_createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i]);
}
}
/*******************************************************************************
EXTERNAL FUNCTIONS
*******************************************************************************/
/**
* @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum
* operator capacity after the operator is registered, this method will fail.
* @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for
* @param socket is the socket of the operator (typically an IP address)
* @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership
* @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager
* @dev `params` is ignored if the caller has previously registered a public key
* @dev `operatorSignature` is ignored if the operator's status is already REGISTERED
*/
function registerOperator(
bytes calldata quorumNumbers,
string calldata socket,
IBLSApkRegistry.PubkeyRegistrationParams calldata params,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
/**
* If the operator has NEVER registered a pubkey before, use `params` to register
* their pubkey in blsApkRegistry
*
* If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash
* (operatorId) is fetched instead
*/
bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params);
// Register the operator in each of the registry contracts and update the operator's
// quorum bitmap and registration status
uint32[] memory numOperatorsPerQuorum = _registerOperator({
operator: msg.sender,
operatorId: operatorId,
quorumNumbers: quorumNumbers,
socket: socket,
operatorSignature: operatorSignature
}).numOperatorsPerQuorum;
// For each quorum, validate that the new operator count does not exceed the maximum
// (If it does, an operator needs to be replaced -- see `registerOperatorWithChurn`)
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
require(
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount,
"RegistryCoordinator.registerOperator: operator count exceeds maximum"
);
}
}
/**
* @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator
* capacity, `operatorKickParams` is used to replace an old operator with the new one.
* @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for
* @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership
* @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the
* operator registers for quorums
* @param churnApproverSignature is the signature of the churnApprover over the `operatorKickParams`
* @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager
* @dev `params` is ignored if the caller has previously registered a public key
* @dev `operatorSignature` is ignored if the operator's status is already REGISTERED
*/
function registerOperatorWithChurn(
bytes calldata quorumNumbers,
string calldata socket,
IBLSApkRegistry.PubkeyRegistrationParams calldata params,
OperatorKickParam[] calldata operatorKickParams,
SignatureWithSaltAndExpiry memory churnApproverSignature,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch");
/**
* If the operator has NEVER registered a pubkey before, use `params` to register
* their pubkey in blsApkRegistry
*
* If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash
* (operatorId) is fetched instead
*/
bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params);
// Verify the churn approver's signature for the registering operator and kick params
_verifyChurnApproverSignature({
registeringOperator: msg.sender,
registeringOperatorId: operatorId,
operatorKickParams: operatorKickParams,
churnApproverSignature: churnApproverSignature
});
// Register the operator in each of the registry contracts and update the operator's
// quorum bitmap and registration status
RegisterResults memory results = _registerOperator({
operator: msg.sender,
operatorId: operatorId,
quorumNumbers: quorumNumbers,
socket: socket,
operatorSignature: operatorSignature
});
// Check that each quorum's operator count is below the configured maximum. If the max
// is exceeded, use `operatorKickParams` to deregister an existing operator to make space
for (uint256 i = 0; i < quorumNumbers.length; i++) {
OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])];
/**
* If the new operator count for any quorum exceeds the maximum, validate
* that churn can be performed, then deregister the specified operator
*/
if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) {
_validateChurn({
quorumNumber: uint8(quorumNumbers[i]),
totalQuorumStake: results.totalStakes[i],
newOperator: msg.sender,
newOperatorStake: results.operatorStakes[i],
kickParams: operatorKickParams[i],
setParams: operatorSetParams
});
_deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]);
}
}
}
/**
* @notice Deregisters the caller from one or more quorums
* @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from
*/
function deregisterOperator(
bytes calldata quorumNumbers
) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) {
_deregisterOperator({
operator: msg.sender,
quorumNumbers: quorumNumbers
});
}
/**
* @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator
* is found to be below the minimum stake for the quorum, they are deregistered.
* @dev stakes are queried from the Eigenlayer core DelegationManager contract
* @param operators a list of operator addresses to update
*/
function updateOperators(address[] calldata operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) {
for (uint256 i = 0; i < operators.length; i++) {
address operator = operators[i];
OperatorInfo memory operatorInfo = _operatorInfo[operator];
bytes32 operatorId = operatorInfo.operatorId;
// Update the operator's stake for their active quorums
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
bytes memory quorumsToUpdate = BitmapUtils.bitmapToBytesArray(currentBitmap);
_updateOperator(operator, operatorInfo, quorumsToUpdate);
}
}
/**
* @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes.
* Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered
* operators were updated.
* @dev stakes are queried from the Eigenlayer core DelegationManager contract
* @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update.
* @dev Each list of operator addresses MUST be sorted in ascending order
* @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum
* @param quorumNumbers is an ordered byte array containing the quorum numbers being updated
* @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber`
* for the corresponding quorum.
* @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to
* this method is broadcast (but before it is executed), the method will fail
*/
function updateOperatorsForQuorum(
address[][] calldata operatorsPerQuorum,
bytes calldata quorumNumbers
) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) {
// Input validation
// - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap)
// - there should be no duplicates in `quorumNumbers`
// - there should be one list of operators per quorum
uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
require(
operatorsPerQuorum.length == quorumNumbers.length,
"RegistryCoordinator.updateOperatorsForQuorum: input length mismatch"
);
// For each quorum, update ALL registered operators
for (uint256 i = 0; i < quorumNumbers.length; ++i) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
// Ensure we've passed in the correct number of operators for this quorum
address[] calldata currQuorumOperators = operatorsPerQuorum[i];
require(
currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber),
"RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total"
);
address prevOperatorAddress = address(0);
// For each operator:
// - check that they are registered for this quorum
// - check that their address is strictly greater than the last operator
// ... then, update their stakes
for (uint256 j = 0; j < currQuorumOperators.length; ++j) {
address operator = currQuorumOperators[j];
OperatorInfo memory operatorInfo = _operatorInfo[operator];
bytes32 operatorId = operatorInfo.operatorId;
{
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
// Check that the operator is registered
require(
BitmapUtils.isSet(currentBitmap, quorumNumber),
"RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum"
);
// Prevent duplicate operators
require(
operator > prevOperatorAddress,
"RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order"
);
}
// Update the operator
_updateOperator(operator, operatorInfo, quorumNumbers[i:i+1]);
prevOperatorAddress = operator;
}
// Update timestamp that all operators in quorum have been updated all at once
quorumUpdateBlockNumber[quorumNumber] = block.number;
emit QuorumBlockNumberUpdated(quorumNumber, block.number);
}
}
/**
* @notice Updates the socket of the msg.sender given they are a registered operator
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external {
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered");
emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket);
}
/*******************************************************************************
EXTERNAL FUNCTIONS - EJECTOR
*******************************************************************************/
/**
* @notice Forcibly deregisters an operator from one or more quorums
* @param operator the operator to eject
* @param quorumNumbers the quorum numbers to eject the operator from
*/
function ejectOperator(
address operator,
bytes calldata quorumNumbers
) external onlyEjector {
_deregisterOperator({
operator: operator,
quorumNumbers: quorumNumbers
});
}
/*******************************************************************************
EXTERNAL FUNCTIONS - OWNER
*******************************************************************************/
/**
* @notice Creates a quorum and initializes it in each registry contract
* @param operatorSetParams configures the quorum's max operator count and churn parameters
* @param minimumStake sets the minimum stake required for an operator to register or remain
* registered
* @param strategyParams a list of strategies and multipliers used by the StakeRegistry to
* calculate an operator's stake weight for the quorum
*/
function createQuorum(
OperatorSetParam memory operatorSetParams,
uint96 minimumStake,
IStakeRegistry.StrategyParams[] memory strategyParams
) external virtual onlyOwner {
_createQuorum(operatorSetParams, minimumStake, strategyParams);
}
/**
* @notice Updates an existing quorum's configuration with a new max operator count
* and operator churn parameters
* @param quorumNumber the quorum number to update
* @param operatorSetParams the new config
* @dev only callable by the owner
*/
function setOperatorSetParams(
uint8 quorumNumber,
OperatorSetParam memory operatorSetParams
) external onlyOwner quorumExists(quorumNumber) {
_setOperatorSetParams(quorumNumber, operatorSetParams);
}
/**
* @notice Sets the churnApprover, which approves operator registration with churn
* (see `registerOperatorWithChurn`)
* @param _churnApprover the new churn approver
* @dev only callable by the owner
*/
function setChurnApprover(address _churnApprover) external onlyOwner {
_setChurnApprover(_churnApprover);
}
/**
* @notice Sets the ejector, which can force-deregister operators from quorums
* @param _ejector the new ejector
* @dev only callable by the owner
*/
function setEjector(address _ejector) external onlyOwner {
_setEjector(_ejector);
}
/*******************************************************************************
INTERNAL FUNCTIONS
*******************************************************************************/
struct RegisterResults {
uint32[] numOperatorsPerQuorum;
uint96[] operatorStakes;
uint96[] totalStakes;
}
/**
* @notice Register the operator for one or more quorums. This method updates the
* operator's quorum bitmap, socket, and status, then registers them with each registry.
*/
function _registerOperator(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers,
string memory socket,
SignatureWithSaltAndExpiry memory operatorSignature
) internal virtual returns (RegisterResults memory results) {
/**
* Get bitmap of quorums to register for and operator's current bitmap. Validate that:
* - we're trying to register for at least 1 quorum
* - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap)
* - the operator is not currently registered for any quorums we're registering for
* Then, calculate the operator's new bitmap after registration
*/
uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0");
require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for");
uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd));
/**
* Update operator's bitmap, socket, and status. Only update operatorInfo if needed:
* if we're `REGISTERED`, the operatorId and status are already correct.
*/
_updateOperatorBitmap({
operatorId: operatorId,
newBitmap: newBitmap
});
emit OperatorSocketUpdate(operatorId, socket);
// If the operator wasn't registered for any quorums, update their status
// and register them with this AVS in EigenLayer core (DelegationManager)
if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) {
_operatorInfo[operator] = OperatorInfo({
operatorId: operatorId,
status: OperatorStatus.REGISTERED
});
// Register the operator with the EigenLayer core contracts via this AVS's ServiceManager
serviceManager.registerOperatorToAVS(operator, operatorSignature);
emit OperatorRegistered(operator, operatorId);
}
// Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry
blsApkRegistry.registerOperator(operator, quorumNumbers);
(results.operatorStakes, results.totalStakes) =
stakeRegistry.registerOperator(operator, operatorId, quorumNumbers);
results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers);
return results;
}
/**
* @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the
* operator has not registered a pubkey, attempts to register a pubkey using
* `params`
* @param operator the operator whose pubkey to query from the BLSApkRegistry
* @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership
* @dev `params` can be empty if the operator has already registered a pubkey in the BLSApkRegistry
*/
function _getOrCreateOperatorId(
address operator,
IBLSApkRegistry.PubkeyRegistrationParams calldata params
) internal returns (bytes32 operatorId) {
operatorId = blsApkRegistry.getOperatorId(operator);
if (operatorId == 0) {
operatorId = blsApkRegistry.registerBLSPublicKey(operator, params, pubkeyRegistrationMessageHash(operator));
}
return operatorId;
}
/**
* @notice Validates that an incoming operator is eligible to replace an existing
* operator based on the stake of both
* @dev In order to churn, the incoming operator needs to have more stake than the
* existing operator by a proportion given by `kickBIPsOfOperatorStake`
* @dev In order to be churned out, the existing operator needs to have a proportion
* of the total quorum stake less than `kickBIPsOfTotalStake`
* @param quorumNumber `newOperator` is trying to replace an operator in this quorum
* @param totalQuorumStake the total stake of all operators in the quorum, after the
* `newOperator` registers
* @param newOperator the incoming operator
* @param newOperatorStake the incoming operator's stake
* @param kickParams the quorum number and existing operator to replace
* @dev the existing operator's registration to this quorum isn't checked here, but
* if we attempt to deregister them, this will be checked in `_deregisterOperator`
* @param setParams config for this quorum containing `kickBIPsX` stake proportions
* mentioned above
*/
function _validateChurn(
uint8 quorumNumber,
uint96 totalQuorumStake,
address newOperator,
uint96 newOperatorStake,
OperatorKickParam memory kickParams,
OperatorSetParam memory setParams
) internal view {
address operatorToKick = kickParams.operator;
bytes32 idToKick = _operatorInfo[operatorToKick].operatorId;
require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self");
require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorumNumber not the same as signed");
// Get the target operator's stake and check that it is below the kick thresholds
uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber);
require(
newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams),
"RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"
);
require(
operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams),
"RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"
);
}
/**
* @dev Deregister the operator from one or more quorums
* This method updates the operator's quorum bitmap and status, then deregisters
* the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry
*/
function _deregisterOperator(
address operator,
bytes memory quorumNumbers
) internal virtual {
// Fetch the operator's info and ensure they are registered
OperatorInfo storage operatorInfo = _operatorInfo[operator];
bytes32 operatorId = operatorInfo.operatorId;
require(operatorInfo.status == OperatorStatus.REGISTERED, "RegistryCoordinator._deregisterOperator: operator is not registered");
/**
* Get bitmap of quorums to deregister from and operator's current bitmap. Validate that:
* - we're trying to deregister from at least 1 quorum
* - the quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap)
* - the operator is currently registered for any quorums we're trying to deregister from
* Then, calculate the operator's new bitmap after deregistration
*/
uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0");
require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums");
uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove));
// Update operator's bitmap and status
_updateOperatorBitmap({
operatorId: operatorId,
newBitmap: newBitmap
});
// If the operator is no longer registered for any quorums, update their status and deregister
// them from the AVS via the EigenLayer core contracts
if (newBitmap.isEmpty()) {
operatorInfo.status = OperatorStatus.DEREGISTERED;
serviceManager.deregisterOperatorFromAVS(operator);
emit OperatorDeregistered(operator, operatorId);
}
// Deregister operator with each of the registry contracts
blsApkRegistry.deregisterOperator(operator, quorumNumbers);
stakeRegistry.deregisterOperator(operatorId, quorumNumbers);
indexRegistry.deregisterOperator(operatorId, quorumNumbers);
}
/**
* @notice Updates the StakeRegistry's view of the operator's stake in one or more quorums.
* For any quorums where the StakeRegistry finds the operator is under the configured minimum
* stake, `quorumsToRemove` is returned and used to deregister the operator from those quorums
* @dev does nothing if operator is not registered for any quorums.
*/
function _updateOperator(
address operator,
OperatorInfo memory operatorInfo,
bytes memory quorumsToUpdate
) internal {
if (operatorInfo.status != OperatorStatus.REGISTERED) {
return;
}
bytes32 operatorId = operatorInfo.operatorId;
uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate);
if (!quorumsToRemove.isEmpty()) {
_deregisterOperator({
operator: operator,
quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove)
});
}
}
/**
* @notice Returns the stake threshold required for an incoming operator to replace an existing operator
* The incoming operator must have more stake than the return value.
*/
function _individualKickThreshold(uint96 operatorStake, OperatorSetParam memory setParams) internal pure returns (uint96) {
return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR;
}
/**
* @notice Returns the total stake threshold required for an operator to remain in a quorum.
* The operator must have at least the returned stake amount to keep their position.
*/
function _totalKickThreshold(uint96 totalStake, OperatorSetParam memory setParams) internal pure returns (uint96) {
return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR;
}
/// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce
function _verifyChurnApproverSignature(
address registeringOperator,
bytes32 registeringOperatorId,
OperatorKickParam[] memory operatorKickParams,
SignatureWithSaltAndExpiry memory churnApproverSignature
) internal {
// make sure the salt hasn't been used already
require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used");
require(churnApproverSignature.expiry >= block.timestamp, "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired");
// set salt used to true
isChurnApproverSaltUsed[churnApproverSignature.salt] = true;
// check the churnApprover's signature
EIP1271SignatureUtils.checkSignature_EIP1271(
churnApprover,
calculateOperatorChurnApprovalDigestHash(registeringOperator, registeringOperatorId, operatorKickParams, churnApproverSignature.salt, churnApproverSignature.expiry),
churnApproverSignature.signature
);
}
/**
* @notice Creates a quorum and initializes it in each registry contract
* @param operatorSetParams configures the quorum's max operator count and churn parameters
* @param minimumStake sets the minimum stake required for an operator to register or remain
* registered
* @param strategyParams a list of strategies and multipliers used by the StakeRegistry to
* calculate an operator's stake weight for the quorum
*/
function _createQuorum(
OperatorSetParam memory operatorSetParams,
uint96 minimumStake,
IStakeRegistry.StrategyParams[] memory strategyParams
) internal {
// Increment the total quorum count. Fails if we're already at the max
uint8 prevQuorumCount = quorumCount;
require(prevQuorumCount < MAX_QUORUM_COUNT, "RegistryCoordinator.createQuorum: max quorums reached");
quorumCount = prevQuorumCount + 1;
// The previous count is the new quorum's number
uint8 quorumNumber = prevQuorumCount;
// Initialize the quorum here and in each registry
_setOperatorSetParams(quorumNumber, operatorSetParams);
stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams);
indexRegistry.initializeQuorum(quorumNumber);
blsApkRegistry.initializeQuorum(quorumNumber);
}
/**
* @notice Record an update to an operator's quorum bitmap.
* @param newBitmap is the most up-to-date set of bitmaps the operator is registered for
*/
function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal {
uint256 historyLength = _operatorBitmapHistory[operatorId].length;
if (historyLength == 0) {
// No prior bitmap history - push our first entry
_operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
quorumBitmap: newBitmap
}));
} else {
// We have prior history - fetch our last-recorded update
QuorumBitmapUpdate storage lastUpdate = _operatorBitmapHistory[operatorId][historyLength - 1];
/**
* If the last update was made in the current block, update the entry.
* Otherwise, push a new entry and update the previous entry's "next" field
*/
if (lastUpdate.updateBlockNumber == uint32(block.number)) {
lastUpdate.quorumBitmap = newBitmap;
} else {
lastUpdate.nextUpdateBlockNumber = uint32(block.number);
_operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
quorumBitmap: newBitmap
}));
}
}
}
/// @notice Get the most recent bitmap for the operator, returning an empty bitmap if
/// the operator is not registered.
function _currentOperatorBitmap(bytes32 operatorId) internal view returns (uint192) {
uint256 historyLength = _operatorBitmapHistory[operatorId].length;
if (historyLength == 0) {
return 0;
} else {
return _operatorBitmapHistory[operatorId][historyLength - 1].quorumBitmap;
}
}
/**
* @notice Returns the index of the quorumBitmap for the provided `operatorId` at the given `blockNumber`
* @dev Reverts if the operator had not yet (ever) registered at `blockNumber`
* @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function
*/
function _getQuorumBitmapIndexAtBlockNumber(
uint32 blockNumber,
bytes32 operatorId
) internal view returns (uint32 index) {
uint256 length = _operatorBitmapHistory[operatorId].length;
// Traverse the operator's bitmap history in reverse, returning the first index
// corresponding to an update made before or at `blockNumber`
for (uint256 i = 0; i < length; i++) {
index = uint32(length - i - 1);
if (_operatorBitmapHistory[operatorId][index].updateBlockNumber <= blockNumber) {
return index;
}
}
revert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
);
}
function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal {
_quorumParams[quorumNumber] = operatorSetParams;
emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams);
}
function _setChurnApprover(address newChurnApprover) internal {
emit ChurnApproverUpdated(churnApprover, newChurnApprover);
churnApprover = newChurnApprover;
}
function _setEjector(address newEjector) internal {
emit EjectorUpdated(ejector, newEjector);
ejector = newEjector;
}
/*******************************************************************************
VIEW FUNCTIONS
*******************************************************************************/
/// @notice Returns the operator set params for the given `quorumNumber`
function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) {
return _quorumParams[quorumNumber];
}
/// @notice Returns the operator struct for the given `operator`
function getOperator(address operator) external view returns (OperatorInfo memory) {
return _operatorInfo[operator];
}
/// @notice Returns the operatorId for the given `operator`
function getOperatorId(address operator) external view returns (bytes32) {
return _operatorInfo[operator].operatorId;
}
/// @notice Returns the operator address for the given `operatorId`
function getOperatorFromId(bytes32 operatorId) external view returns (address) {
return blsApkRegistry.getOperatorFromPubkeyHash(operatorId);
}
/// @notice Returns the status for the given `operator`
function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus) {
return _operatorInfo[operator].status;
}
/**
* @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber`
* @dev Reverts if any of the `operatorIds` was not (yet) registered at `blockNumber`
* @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function
*/
function getQuorumBitmapIndicesAtBlockNumber(
uint32 blockNumber,
bytes32[] memory operatorIds
) external view returns (uint32[] memory) {
uint32[] memory indices = new uint32[](operatorIds.length);
for (uint256 i = 0; i < operatorIds.length; i++) {
indices[i] = _getQuorumBitmapIndexAtBlockNumber(blockNumber, operatorIds[i]);
}
return indices;
}
/**
* @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`,
* reverting if `index` is incorrect
* @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which
* helps off-chain processes to fetch the correct `index` input
*/
function getQuorumBitmapAtBlockNumberByIndex(
bytes32 operatorId,
uint32 blockNumber,
uint256 index
) external view returns (uint192) {
QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index];
/**
* Validate that the update is valid for the given blockNumber:
* - blockNumber should be >= the update block number
* - the next update block number should be either 0 or strictly greater than blockNumber
*/
require(
blockNumber >= quorumBitmapUpdate.updateBlockNumber,
"RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"
);
require(
quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber,
"RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"
);
return quorumBitmapUpdate.quorumBitmap;
}
/// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history
function getQuorumBitmapUpdateByIndex(
bytes32 operatorId,
uint256 index
) external view returns (QuorumBitmapUpdate memory) {
return _operatorBitmapHistory[operatorId][index];
}
/// @notice Returns the current quorum bitmap for the given `operatorId` or 0 if the operator is not registered for any quorum
function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) {
return _currentOperatorBitmap(operatorId);
}
/// @notice Returns the length of the quorum bitmap history for the given `operatorId`
function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256) {
return _operatorBitmapHistory[operatorId].length;
}
/// @notice Returns the number of registries
function numRegistries() external view returns (uint256) {
return registries.length;
}
/**
* @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums
* @param registeringOperatorId The id of the registering operator
* @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps
* @param salt The salt to use for the churnApprover's signature
* @param expiry The desired expiry time of the churnApprover's signature
*/
function calculateOperatorChurnApprovalDigestHash(
address registeringOperator,
bytes32 registeringOperatorId,
OperatorKickParam[] memory operatorKickParams,
bytes32 salt,
uint256 expiry
) public view returns (bytes32) {
// calculate the digest hash
return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperator, registeringOperatorId, operatorKickParams, salt, expiry)));
}
/**
* @notice Returns the message hash that an operator must sign to register their BLS public key.
* @param operator is the address of the operator registering their BLS public key
*/
function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) {
return BN254.hashToG1(
_hashTypedDataV4(
keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))
)
);
}
/// @dev need to override function here since its defined in both these contracts
function owner()
public
view
override(OwnableUpgradeable, IRegistryCoordinator)
returns (address)
{
return OwnableUpgradeable.owner();
}
}
IPauserRegistry.sol 19 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Interface for the `PauserRegistry` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IPauserRegistry {
event PauserStatusChanged(address pauser, bool canPause);
event UnpauserChanged(address previousUnpauser, address newUnpauser);
/// @notice Mapping of addresses to whether they hold the pauser role.
function isPauser(address pauser) external view returns (bool);
/// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
function unpauser() external view returns (address);
}
ISignatureUtils.sol 27 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title The interface for common signature utilities.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface ISignatureUtils {
// @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management.
struct SignatureWithExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
// @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management.
struct SignatureWithSaltAndExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the salt used to generate the signature
bytes32 salt;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
}
ISocketUpdater.sol 20 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
/**
* @title Interface for an `ISocketUpdater` where operators can update their sockets.
* @author Layr Labs, Inc.
*/
interface ISocketUpdater {
// EVENTS
event OperatorSocketUpdate(bytes32 indexed operatorId, string socket);
// FUNCTIONS
/**
* @notice Updates the socket of the msg.sender given they are a registered operator
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external;
}
IBLSApkRegistry.sol 142 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import {IRegistry} from "./IRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
/**
* @title Minimal interface for a registry that keeps track of aggregate operator public keys across many quorums.
* @author Layr Labs, Inc.
*/
interface IBLSApkRegistry is IRegistry {
// STRUCTS
/// @notice Data structure used to track the history of the Aggregate Public Key of all operators
struct ApkUpdate {
// first 24 bytes of keccak256(apk_x0, apk_x1, apk_y0, apk_y1)
bytes24 apkHash;
// block number at which the update occurred
uint32 updateBlockNumber;
// block number at which the next update occurred
uint32 nextUpdateBlockNumber;
}
/**
* @notice Struct used when registering a new public key
* @param pubkeyRegistrationSignature is the registration message signed by the private key of the operator
* @param pubkeyG1 is the corresponding G1 public key of the operator
* @param pubkeyG2 is the corresponding G2 public key of the operator
*/
struct PubkeyRegistrationParams {
BN254.G1Point pubkeyRegistrationSignature;
BN254.G1Point pubkeyG1;
BN254.G2Point pubkeyG2;
}
// EVENTS
/// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`.
event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2);
// @notice Emitted when a new operator pubkey is registered for a set of quorums
event OperatorAddedToQuorums(
address operator,
bytes32 operatorId,
bytes quorumNumbers
);
// @notice Emitted when an operator pubkey is removed from a set of quorums
event OperatorRemovedFromQuorums(
address operator,
bytes32 operatorId,
bytes quorumNumbers
);
/**
* @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`.
* @param operator The address of the operator to register.
* @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(address operator, bytes calldata quorumNumbers) external;
/**
* @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`.
* @param operator The address of the operator to deregister.
* @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(address operator, bytes calldata quorumNumbers) external;
/**
* @notice Initializes a new quorum by pushing its first apk update
* @param quorumNumber The number of the new quorum
*/
function initializeQuorum(uint8 quorumNumber) external;
/**
* @notice mapping from operator address to pubkey hash.
* Returns *zero* if the `operator` has never registered, and otherwise returns the hash of the public key of the operator.
*/
function operatorToPubkeyHash(address operator) external view returns (bytes32);
/**
* @notice mapping from pubkey hash to operator address.
* Returns *zero* if no operator has ever registered the public key corresponding to `pubkeyHash`,
* and otherwise returns the (unique) registered operator who owns the BLS public key that is the preimage of `pubkeyHash`.
*/
function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address);
/**
* @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key.
* @param operator is the operator for whom the key is being registered
* @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership
* @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership
*/
function registerBLSPublicKey(
address operator,
PubkeyRegistrationParams calldata params,
BN254.G1Point calldata pubkeyRegistrationMessageHash
) external returns (bytes32 operatorId);
/**
* @notice Returns the pubkey and pubkey hash of an operator
* @dev Reverts if the operator has not registered a valid pubkey
*/
function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32);
/// @notice Returns the current APK for the provided `quorumNumber `
function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory);
/// @notice Returns the index of the quorumApk index at `blockNumber` for the provided `quorumNumber`
function getApkIndicesAtBlockNumber(bytes calldata quorumNumbers, uint256 blockNumber) external view returns(uint32[] memory);
/// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber`
function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory);
/// @notice Returns the operator address for the given `pubkeyHash`
function getOperatorFromPubkeyHash(bytes32 pubkeyHash) external view returns (address);
/**
* @notice get 24 byte hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`;
* called by checkSignatures in BLSSignatureChecker.sol.
* @param quorumNumber is the quorum whose ApkHash is being retrieved
* @param blockNumber is the number of the block for which the latest ApkHash will be retrieved
* @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage
*/
function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24);
/// @notice returns the ID used to identify the `operator` within this AVS.
/// @dev Returns zero in the event that the `operator` has never registered for the AVS
function getOperatorId(address operator) external view returns (bytes32);
}
IStakeRegistry.sol 248 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IRegistry} from "./IRegistry.sol";
/**
* @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums.
* @author Layr Labs, Inc.
*/
interface IStakeRegistry is IRegistry {
// DATA STRUCTURES
/// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage
struct StakeUpdate {
// the block number at which the stake amounts were updated and stored
uint32 updateBlockNumber;
// the block number at which the *next update* occurred.
/// @notice This entry has the value **0** until another update takes place.
uint32 nextUpdateBlockNumber;
// stake weight for the quorum
uint96 stake;
}
/**
* @notice In weighing a particular strategy, the amount of underlying asset for that strategy is
* multiplied by its multiplier, then divided by WEIGHTING_DIVISOR
*/
struct StrategyParams {
IStrategy strategy;
uint96 multiplier;
}
// EVENTS
/// @notice emitted whenever the stake of `operator` is updated
event OperatorStakeUpdate(
bytes32 indexed operatorId,
uint8 quorumNumber,
uint96 stake
);
/// @notice emitted when the minimum stake for a quorum is updated
event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake);
/// @notice emitted when a new quorum is created
event QuorumCreated(uint8 indexed quorumNumber);
/// @notice emitted when `strategy` has been added to the array at `strategyParams[quorumNumber]`
event StrategyAddedToQuorum(uint8 indexed quorumNumber, IStrategy strategy);
/// @notice emitted when `strategy` has removed from the array at `strategyParams[quorumNumber]`
event StrategyRemovedFromQuorum(uint8 indexed quorumNumber, IStrategy strategy);
/// @notice emitted when `strategy` has its `multiplier` updated in the array at `strategyParams[quorumNumber]`
event StrategyMultiplierUpdated(uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier);
/**
* @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`.
* @param operator The address of the operator to register.
* @param operatorId The id of the operator to register.
* @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber.
* @return The operator's current stake for each quorum, and the total stake for each quorum
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(
address operator,
bytes32 operatorId,
bytes memory quorumNumbers
) external returns (uint96[] memory, uint96[] memory);
/**
* @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`.
* @param operatorId The id of the operator to deregister.
* @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) external;
/**
* @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake
*/
function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external;
/// @notice Adds new strategies and the associated multipliers to the @param quorumNumber.
function addStrategies(
uint8 quorumNumber,
StrategyParams[] memory strategyParams
) external;
/**
* @notice This function is used for removing strategies and their associated weights from the
* mapping strategyParams for a specific @param quorumNumber.
* @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise
* the removal of lower index entries will cause a shift in the indices of the other strategiesToRemove
*/
function removeStrategies(uint8 quorumNumber, uint256[] calldata indicesToRemove) external;
/**
* @notice This function is used for modifying the weights of strategies that are already in the
* mapping strategyParams for a specific
* @param quorumNumber is the quorum number to change the strategy for
* @param strategyIndices are the indices of the strategies to change
* @param newMultipliers are the new multipliers for the strategies
*/
function modifyStrategyParams(
uint8 quorumNumber,
uint256[] calldata strategyIndices,
uint96[] calldata newMultipliers
) external;
/// @notice Constant used as a divisor in calculating weights.
function WEIGHTING_DIVISOR() external pure returns (uint256);
/// @notice Returns the EigenLayer delegation manager contract.
function delegation() external view returns (IDelegationManager);
/// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]`
function minimumStakeForQuorum(uint8 quorumNumber) external view returns (uint96);
/// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`.
function strategyParamsLength(uint8 quorumNumber) external view returns (uint256);
/// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber`
function strategyParamsByIndex(
uint8 quorumNumber,
uint256 index
) external view returns (StrategyParams memory);
/**
* @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber.
* @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount`
*/
function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external view returns (uint96);
/**
* @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array.
* @param operatorId The id of the operator of interest.
* @param quorumNumber The quorum number to get the stake for.
*/
function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate[] memory);
function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256);
/**
* @notice Returns the `index`-th entry in the dynamic array of total stake, `totalStakeHistory` for quorum `quorumNumber`.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`.
*/
function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (StakeUpdate memory);
/// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber`
function getStakeUpdateIndexAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber)
external
view
returns (uint32);
/// @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber`
function getTotalStakeIndicesAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) ;
/**
* @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index)
external
view
returns (StakeUpdate memory);
/**
* @notice Returns the most recent stake weight for the `operatorId` for a certain quorum
* @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history
*/
function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate memory);
/**
* @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the
* `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry
* corresponds to the operator's stake at `blockNumber`. Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
* @dev used the BLSSignatureChecker to get past stakes of signing operators
*/
function getStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index)
external
view
returns (uint96);
/**
* @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the
* `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`.
* Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
* @dev used the BLSSignatureChecker to get past stakes of signing operators
*/
function getTotalStakeAtBlockNumberFromIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (uint96);
/**
* @notice Returns the most recent stake weight for the `operatorId` for quorum `quorumNumber`
* @dev Function returns weight of **0** in the event that the operator has no stake history
*/
function getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96);
/// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber`
function getStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber)
external
view
returns (uint96);
/**
* @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`.
* @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty.
*/
function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96);
/**
* @notice Called by the registry coordinator to update an operator's stake for one
* or more quorums.
*
* If the operator no longer has the minimum stake required for a quorum, they are
* added to the
* @return A bitmap of quorums where the operator no longer meets the minimum stake
* and should be deregistered.
*/
function updateOperatorStake(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) external returns (uint192);
}
IIndexRegistry.sol 90 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import {IRegistry} from "./IRegistry.sol";
/**
* @title Interface for a `Registry`-type contract that keeps track of an ordered list of operators for up to 256 quorums.
* @author Layr Labs, Inc.
*/
interface IIndexRegistry is IRegistry {
// EVENTS
// emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated
event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex);
// DATA STRUCTURES
// struct used to give definitive ordering to operators at each blockNumber.
struct OperatorUpdate {
// blockNumber number from which `operatorIndex` was the operators index
// the operator's index is the first entry such that `blockNumber >= entry.fromBlockNumber`
uint32 fromBlockNumber;
// the operator at this index
bytes32 operatorId;
}
// struct used to denote the number of operators in a quorum at a given blockNumber
struct QuorumUpdate {
// The total number of operators at a `blockNumber` is the first entry such that `blockNumber >= entry.fromBlockNumber`
uint32 fromBlockNumber;
// The number of operators at `fromBlockNumber`
uint32 numOperators;
}
/**
* @notice Registers the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`.
* @param operatorId is the id of the operator that is being registered
* @param quorumNumbers is the quorum numbers the operator is registered for
* @return numOperatorsPerQuorum is a list of the number of operators (including the registering operator) in each of the quorums the operator is registered for
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(bytes32 operatorId, bytes calldata quorumNumbers) external returns(uint32[] memory);
/**
* @notice Deregisters the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`.
* @param operatorId is the id of the operator that is being deregistered
* @param quorumNumbers is the quorum numbers the operator is deregistered for
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(bytes32 operatorId, bytes calldata quorumNumbers) external;
/**
* @notice Initialize a quorum by pushing its first quorum update
* @param quorumNumber The number of the new quorum
*/
function initializeQuorum(uint8 quorumNumber) external;
/// @notice Returns the OperatorUpdate entry for the specified `operatorIndex` and `quorumNumber` at the specified `arrayIndex`
function getOperatorUpdateAtIndex(
uint8 quorumNumber,
uint32 operatorIndex,
uint32 arrayIndex
) external view returns (OperatorUpdate memory);
/// @notice Returns the QuorumUpdate entry for the specified `quorumNumber` at the specified `quorumIndex`
function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 quorumIndex) external view returns (QuorumUpdate memory);
/// @notice Returns the most recent OperatorUpdate entry for the specified quorumNumber and operatorIndex
function getLatestOperatorUpdate(uint8 quorumNumber, uint32 operatorIndex) external view returns (OperatorUpdate memory);
/// @notice Returns the most recent QuorumUpdate entry for the specified quorumNumber
function getLatestQuorumUpdate(uint8 quorumNumber) external view returns (QuorumUpdate memory);
/// @notice Returns the current number of operators of this service for `quorumNumber`.
function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32);
/// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber`
function getOperatorListAtBlockNumber(uint8 quorumNumber, uint32 blockNumber) external view returns (bytes32[] memory);
}
IServiceManager.sol 53 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
/**
* @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer
* @author Layr Labs, Inc.
*/
interface IServiceManager {
/**
* @notice Updates the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
*/
function updateAVSMetadataURI(string memory _metadataURI) external;
/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS
* @param operator The address of the operator to register.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operator,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external;
/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(address operator) external;
/**
* @notice Returns the list of strategies that the operator has potentially restaked on the AVS
* @param operator The address of the operator to get restaked strategies for
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness
* of each element in the returned array. The off-chain service should do that validation separately
*/
function getOperatorRestakedStrategies(address operator) external view returns (address[] memory);
/**
* @notice Returns the list of strategies that the AVS supports for restaking
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on uniqueness of each element in the returned array.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies() external view returns (address[] memory);
/// @notice Returns the EigenLayer AVSDirectory contract.
function avsDirectory() external view returns (address);
}
IRegistryCoordinator.sol 153 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import {IBLSApkRegistry} from "./IBLSApkRegistry.sol";
import {IStakeRegistry} from "./IStakeRegistry.sol";
import {IIndexRegistry} from "./IIndexRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
/**
* @title Interface for a contract that coordinates between various registries for an AVS.
* @author Layr Labs, Inc.
*/
interface IRegistryCoordinator {
// EVENTS
/// Emits when an operator is registered
event OperatorRegistered(address indexed operator, bytes32 indexed operatorId);
/// Emits when an operator is deregistered
event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId);
event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams);
event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover);
event EjectorUpdated(address prevEjector, address newEjector);
/// @notice emitted when all the operators for a quorum are updated at once
event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber);
// DATA STRUCTURES
enum OperatorStatus
{
// default is NEVER_REGISTERED
NEVER_REGISTERED,
REGISTERED,
DEREGISTERED
}
// STRUCTS
/**
* @notice Data structure for storing info on operators
*/
struct OperatorInfo {
// the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegistry
bytes32 operatorId;
// indicates whether the operator is actively registered for serving the middleware or not
OperatorStatus status;
}
/**
* @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the
* quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber`
* @dev nextUpdateBlockNumber is initialized to 0 for the latest update
*/
struct QuorumBitmapUpdate {
uint32 updateBlockNumber;
uint32 nextUpdateBlockNumber;
uint192 quorumBitmap;
}
/**
* @notice Data structure for storing operator set params for a given quorum. Specifically the
* `maxOperatorCount` is the maximum number of operators that can be registered for the quorum,
* `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum,
* and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked.
*/
struct OperatorSetParam {
uint32 maxOperatorCount;
uint16 kickBIPsOfOperatorStake;
uint16 kickBIPsOfTotalStake;
}
/**
* @notice Data structure for the parameters needed to kick an operator from a quorum with number `quorumNumber`, used during registration churn.
* `operator` is the address of the operator to kick
*/
struct OperatorKickParam {
uint8 quorumNumber;
address operator;
}
/// @notice Returns the operator set params for the given `quorumNumber`
function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory);
/// @notice the Stake registry contract that will keep track of operators' stakes
function stakeRegistry() external view returns (IStakeRegistry);
/// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' BLS aggregate pubkeys per quorum
function blsApkRegistry() external view returns (IBLSApkRegistry);
/// @notice the index Registry contract that will keep track of operators' indexes
function indexRegistry() external view returns (IIndexRegistry);
/**
* @notice Ejects the provided operator from the provided quorums from the AVS
* @param operator is the operator to eject
* @param quorumNumbers are the quorum numbers to eject the operator from
*/
function ejectOperator(
address operator,
bytes calldata quorumNumbers
) external;
/// @notice Returns the number of quorums the registry coordinator has created
function quorumCount() external view returns (uint8);
/// @notice Returns the operator struct for the given `operator`
function getOperator(address operator) external view returns (OperatorInfo memory);
/// @notice Returns the operatorId for the given `operator`
function getOperatorId(address operator) external view returns (bytes32);
/// @notice Returns the operator address for the given `operatorId`
function getOperatorFromId(bytes32 operatorId) external view returns (address operator);
/// @notice Returns the status for the given `operator`
function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus);
/// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber`
function getQuorumBitmapIndicesAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory);
/**
* @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`
* @dev reverts if `index` is incorrect
*/
function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192);
/// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history
function getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory);
/// @notice Returns the current quorum bitmap for the given `operatorId`
function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192);
/// @notice Returns the length of the quorum bitmap history for the given `operatorId`
function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256);
/// @notice Returns the registry at the desired index
function registries(uint256) external view returns (address);
/// @notice Returns the number of registries
function numRegistries() external view returns (uint256);
/**
* @notice Returns the message hash that an operator must sign to register their BLS public key.
* @param operator is the address of the operator registering their BLS public key
*/
function pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory);
/// @notice returns the blocknumber the quorum was last updated all at once for all operators
function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256);
/// @notice The owner of the registry coordinator
function owner() external view returns (address);
}
EIP1271SignatureUtils.sol 41 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/**
* @title Library of utilities for making EIP1271-compliant signature checks.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
library EIP1271SignatureUtils {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e;
/**
* @notice Checks @param signature is a valid signature of @param digestHash from @param signer.
* If the `signer` contains no code -- i.e. it is not (yet, at least) a contract address, then checks using standard ECDSA logic
* Otherwise, passes on the signature to the signer to verify the signature and checks that it returns the `EIP1271_MAGICVALUE`.
*/
function checkSignature_EIP1271(address signer, bytes32 digestHash, bytes memory signature) internal view {
/**
* check validity of signature:
* 1) if `signer` is an EOA, then `signature` must be a valid ECDSA signature from `signer`,
* indicating their intention for this action
* 2) if `signer` is a contract, then `signature` must will be checked according to EIP-1271
*/
if (Address.isContract(signer)) {
require(
IERC1271(signer).isValidSignature(digestHash, signature) == EIP1271_MAGICVALUE,
"EIP1271SignatureUtils.checkSignature_EIP1271: ERC1271 signature verification failed"
);
} else {
require(
ECDSA.recover(digestHash, signature) == signer,
"EIP1271SignatureUtils.checkSignature_EIP1271: signature not from signer"
);
}
}
}
BitmapUtils.sol 199 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
/**
* @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap.
* @author Layr Labs, Inc.
*/
library BitmapUtils {
/**
* @notice Byte arrays are meant to contain unique bytes.
* If the array length exceeds 256, then it's impossible for all entries to be unique.
* This constant captures the max allowed array length (inclusive, i.e. 256 is allowed).
*/
uint256 internal constant MAX_BYTE_ARRAY_LENGTH = 256;
/**
* @notice Converts an ordered array of bytes into a bitmap.
* @param orderedBytesArray The array of bytes to convert/compress into a bitmap. Must be in strictly ascending order.
* @return The resulting bitmap.
* @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap.
* @dev This function will eventually revert in the event that the `orderedBytesArray` is not properly ordered (in ascending order).
* @dev This function will also revert if the `orderedBytesArray` input contains any duplicate entries (i.e. duplicate bytes).
*/
function orderedBytesArrayToBitmap(bytes memory orderedBytesArray) internal pure returns (uint256) {
// sanity-check on input. a too-long input would fail later on due to having duplicate entry(s)
require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH,
"BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long");
// return empty bitmap early if length of array is 0
if (orderedBytesArray.length == 0) {
return uint256(0);
}
// initialize the empty bitmap, to be built inside the loop
uint256 bitmap;
// initialize an empty uint256 to be used as a bitmask inside the loop
uint256 bitMask;
// perform the 0-th loop iteration with the ordering check *omitted* (since it is unnecessary / will always pass)
// construct a single-bit mask from the numerical value of the 0th byte of the array, and immediately add it to the bitmap
bitmap = uint256(1 << uint8(orderedBytesArray[0]));
// loop through each byte in the array to construct the bitmap
for (uint256 i = 1; i < orderedBytesArray.length; ++i) {
// construct a single-bit mask from the numerical value of the next byte of the array
bitMask = uint256(1 << uint8(orderedBytesArray[i]));
// check strictly ascending array ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap)
require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered");
// add the entry to the bitmap
bitmap = (bitmap | bitMask);
}
return bitmap;
}
/**
* @notice Converts an ordered byte array to a bitmap, validating that all bits are less than `bitUpperBound`
* @param orderedBytesArray The array to convert to a bitmap; must be in strictly ascending order
* @param bitUpperBound The exclusive largest bit. Each bit must be strictly less than this value.
* @dev Reverts if bitmap contains a bit greater than or equal to `bitUpperBound`
*/
function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) {
uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray);
require((1 << bitUpperBound) > bitmap,
"BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"
);
return bitmap;
}
/**
* @notice Utility function for checking if a bytes array is strictly ordered, in ascending order.
* @param bytesArray the bytes array of interest
* @return Returns 'true' if the array is ordered in strictly ascending order, and 'false' otherwise.
* @dev This function returns 'true' for the edge case of the `bytesArray` having zero length.
* It also returns 'false' early for arrays with length in excess of MAX_BYTE_ARRAY_LENGTH (i.e. so long that they cannot be strictly ordered)
*/
function isArrayStrictlyAscendingOrdered(bytes calldata bytesArray) internal pure returns (bool) {
// Return early if the array is too long, or has a length of 0
if (bytesArray.length > MAX_BYTE_ARRAY_LENGTH) {
return false;
}
if (bytesArray.length == 0) {
return true;
}
// Perform the 0-th loop iteration by pulling the 0th byte out of the array
bytes1 singleByte = bytesArray[0];
// For each byte, validate that each entry is *strictly greater than* the previous
// If it isn't, return false as the array is not ordered
for (uint256 i = 1; i < bytesArray.length; ++i) {
if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) {
return false;
}
// Pull the next byte out of the array
singleByte = bytesArray[i];
}
return true;
}
/**
* @notice Converts a bitmap into an array of bytes.
* @param bitmap The bitmap to decompress/convert to an array of bytes.
* @return bytesArray The resulting bitmap array of bytes.
* @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap
*/
function bitmapToBytesArray(uint256 bitmap) internal pure returns (bytes memory /*bytesArray*/) {
// initialize an empty uint256 to be used as a bitmask inside the loop
uint256 bitMask;
// allocate only the needed amount of memory
bytes memory bytesArray = new bytes(countNumOnes(bitmap));
// track the array index to assign to
uint256 arrayIndex = 0;
/**
* loop through each index in the bitmap to construct the array,
* but short-circuit the loop if we reach the number of ones and thus are done
* assigning to memory
*/
for (uint256 i = 0; (arrayIndex < bytesArray.length) && (i < 256); ++i) {
// construct a single-bit mask for the i-th bit
bitMask = uint256(1 << i);
// check if the i-th bit is flipped in the bitmap
if (bitmap & bitMask != 0) {
// if the i-th bit is flipped, then add a byte encoding the value 'i' to the `bytesArray`
bytesArray[arrayIndex] = bytes1(uint8(i));
// increment the bytesArray slot since we've assigned one more byte of memory
unchecked{ ++arrayIndex; }
}
}
return bytesArray;
}
/// @return count number of ones in binary representation of `n`
function countNumOnes(uint256 n) internal pure returns (uint16) {
uint16 count = 0;
while (n > 0) {
n &= (n - 1); // Clear the least significant bit (turn off the rightmost set bit).
count++; // Increment the count for each cleared bit (each one encountered).
}
return count; // Return the total count of ones in the binary representation of n.
}
/// @notice Returns `true` if `bit` is in `bitmap`. Returns `false` otherwise.
function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) {
return 1 == ((bitmap >> bit) & 1);
}
/**
* @notice Returns a copy of `bitmap` with `bit` set.
* @dev IMPORTANT: we're dealing with stack values here, so this doesn't modify
* the original bitmap. Using this correctly requires an assignment statement:
* `bitmap = bitmap.setBit(bit);`
*/
function setBit(uint256 bitmap, uint8 bit) internal pure returns (uint256) {
return bitmap | (1 << bit);
}
/**
* @notice Returns true if `bitmap` has no set bits
*/
function isEmpty(uint256 bitmap) internal pure returns (bool) {
return bitmap == 0;
}
/**
* @notice Returns true if `a` and `b` have no common set bits
*/
function noBitsInCommon(uint256 a, uint256 b) internal pure returns (bool) {
return a & b == 0;
}
/**
* @notice Returns true if `a` is a subset of `b`: ALL of the bits in `a` are also in `b`
*/
function isSubsetOf(uint256 a, uint256 b) internal pure returns (bool) {
return a & b == a;
}
/**
* @notice Returns a new bitmap that contains all bits set in either `a` or `b`
* @dev Result is the union of `a` and `b`
*/
function plus(uint256 a, uint256 b) internal pure returns (uint256) {
return a | b;
}
/**
* @notice Returns a new bitmap that clears all set bits of `b` from `a`
* @dev Negates `b` and returns the intersection of the result with `a`
*/
function minus(uint256 a, uint256 b) internal pure returns (uint256) {
return a & ~b;
}
}
BN254.sol 350 lines
// SPDX-License-Identifier: MIT
// several functions are taken or adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol (MIT license):
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license
pragma solidity =0.8.12;
/**
* @title Library for operations on the BN254 elliptic curve.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contains BN254 parameters, common operations (addition, scalar mul, pairing), and BLS signature functionality.
*/
library BN254 {
// modulus for the underlying field F_p of the elliptic curve
uint256 internal constant FP_MODULUS =
21888242871839275222246405745257275088696311157297823662689037894645226208583;
// modulus for the underlying field F_r of the elliptic curve
uint256 internal constant FR_MODULUS =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[1] * i + X[0]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
function generatorG1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
// generator of group G2
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
uint256 internal constant G2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 internal constant G2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 internal constant G2y1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 internal constant G2y0 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
/// @notice returns the G2 generator
/// @dev mind the ordering of the 1s and 0s!
/// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract
/// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)."
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding
function generatorG2() internal pure returns (G2Point memory) {
return G2Point([G2x1, G2x0], [G2y1, G2y0]);
}
// negation of the generator of group G2
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
uint256 internal constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 internal constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;
function negGeneratorG2() internal pure returns (G2Point memory) {
return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]);
}
bytes32 internal constant powersOfTauMerkleRoot =
0x22c998e49752bbb1918ba87d6d59dd0e83620a311ba91dd4b2cc84990b31b56f;
/**
* @param p Some point in G1.
* @return The negation of `p`, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, FP_MODULUS - (p.Y % FP_MODULUS));
}
}
/**
* @return r the sum of two points of G1
*/
function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "ec-add-failed");
}
/**
* @notice an optimized ecMul implementation that takes O(log_2(s)) ecAdds
* @param p the point to multiply
* @param s the scalar to multiply by
* @dev this function is only safe to use if the scalar is 9 bits or less
*/
function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) {
require(s < 2**9, "scalar-too-large");
// if s is 1 return p
if(s == 1) {
return p;
}
// the accumulated product to return
BN254.G1Point memory acc = BN254.G1Point(0, 0);
// the 2^n*p to add to the accumulated product in each iteration
BN254.G1Point memory p2n = p;
// value of most significant bit
uint16 m = 1;
// index of most significant bit
uint8 i = 0;
//loop until we reach the most significant bit
while(s >= m){
unchecked {
// if the current bit is 1, add the 2^n*p to the accumulated product
if ((s >> i) & 1 == 1) {
acc = plus(acc, p2n);
}
// double the 2^n*p for the next iteration
p2n = plus(p2n, p2n);
// increment the index and double the value of the most significant bit
m <<= 1;
++i;
}
}
// return the accumulated product
return acc;
}
/**
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
*/
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "ec-mul-failed");
}
/**
* @return The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
*/
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
) internal view returns (bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];
uint256[12] memory input;
for (uint256 i = 0; i < 2; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, mul(12, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
/**
* @notice This function is functionally the same as pairing(), however it specifies a gas limit
* the user can set, as a precompile may use the entire gas budget if it reverts.
*/
function safePairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
uint256 pairingGas
) internal view returns (bool, bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];
uint256[12] memory input;
for (uint256 i = 0; i < 2; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(pairingGas, 8, input, mul(12, 0x20), out, 0x20)
}
//Out is the output of the pairing precompile, either 0 or 1 based on whether the two pairings are equal.
//Success is true if the precompile actually goes through (aka all inputs are valid)
return (success, out[0] != 0);
}
/// @return hashedG1 the keccak256 hash of the G1 Point
/// @dev used for BLS signatures
function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) {
assembly {
mstore(0, mload(pk))
mstore(0x20, mload(add(0x20, pk)))
hashedG1 := keccak256(0, 0x40)
}
}
/// @return the keccak256 hash of the G2 Point
/// @dev used for BLS signatures
function hashG2Point(
BN254.G2Point memory pk
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1]));
}
/**
* @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol
*/
function hashToG1(bytes32 _x) internal view returns (G1Point memory) {
uint256 beta = 0;
uint256 y = 0;
uint256 x = uint256(_x) % FP_MODULUS;
while (true) {
(beta, y) = findYFromX(x);
// y^2 == beta
if( beta == mulmod(y, y, FP_MODULUS) ) {
return G1Point(x, y);
}
x = addmod(x, 1, FP_MODULUS);
}
return G1Point(0, 0);
}
/**
* Given X, find Y
*
* where y = sqrt(x^3 + b)
*
* Returns: (x^3 + b), y
*/
function findYFromX(uint256 x) internal view returns (uint256, uint256) {
// beta = (x^3 + b) % p
uint256 beta = addmod(mulmod(mulmod(x, x, FP_MODULUS), x, FP_MODULUS), 3, FP_MODULUS);
// y^2 = x^3 + b
// this acts like: y = sqrt(beta) = beta^((p+1) / 4)
uint256 y = expMod(beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS);
return (beta, y);
}
function expMod(uint256 _base, uint256 _exponent, uint256 _modulus) internal view returns (uint256 retval) {
bool success;
uint256[1] memory output;
uint[6] memory input;
input[0] = 0x20; // baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
input[1] = 0x20; // expLen = new(big.Int).SetBytes(getData(input, 32, 32))
input[2] = 0x20; // modLen = new(big.Int).SetBytes(getData(input, 64, 32))
input[3] = _base;
input[4] = _exponent;
input[5] = _modulus;
assembly {
success := staticcall(sub(gas(), 2000), 5, input, 0xc0, output, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "BN254.expMod: call failure");
return output[0];
}
}
OwnableUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
Initializable.sol 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* `initializer` is equivalent to `reinitializer(1)`, so 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.
*
* 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.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
draft-EIP712.sol 104 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
pragma solidity ^0.8.0;
import "./ECDSA.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_CACHED_THIS = address(this);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}
Pausable.sol 136 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import "../interfaces/IPausable.sol";
/**
* @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
* These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
* @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
* Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
* For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
* you can only flip (any number of) switches to off/0 (aka "paused").
* If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
* 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
* 2) update the paused state to this new value
* @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
* indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
*/
contract Pausable is IPausable {
/// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
IPauserRegistry public pauserRegistry;
/// @dev whether or not the contract is currently paused
uint256 private _paused;
uint256 internal constant UNPAUSE_ALL = 0;
uint256 internal constant PAUSE_ALL = type(uint256).max;
/// @notice
modifier onlyPauser() {
require(pauserRegistry.isPauser(msg.sender), "msg.sender is not permissioned as pauser");
_;
}
modifier onlyUnpauser() {
require(msg.sender == pauserRegistry.unpauser(), "msg.sender is not permissioned as unpauser");
_;
}
/// @notice Throws if the contract is paused, i.e. if any of the bits in `_paused` is flipped to 1.
modifier whenNotPaused() {
require(_paused == 0, "Pausable: contract is paused");
_;
}
/// @notice Throws if the `indexed`th bit of `_paused` is 1, i.e. if the `index`th pause switch is flipped.
modifier onlyWhenNotPaused(uint8 index) {
require(!paused(index), "Pausable: index is paused");
_;
}
/// @notice One-time function for setting the `pauserRegistry` and initializing the value of `_paused`.
function _initializePauser(IPauserRegistry _pauserRegistry, uint256 initPausedStatus) internal {
require(
address(pauserRegistry) == address(0) && address(_pauserRegistry) != address(0),
"Pausable._initializePauser: _initializePauser() can only be called once"
);
_paused = initPausedStatus;
emit Paused(msg.sender, initPausedStatus);
_setPauserRegistry(_pauserRegistry);
}
/**
* @notice This function is used to pause an EigenLayer contract's functionality.
* It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
*/
function pause(uint256 newPausedStatus) external onlyPauser {
// verify that the `newPausedStatus` does not *unflip* any bits (i.e. doesn't unpause anything, all 1 bits remain)
require((_paused & newPausedStatus) == _paused, "Pausable.pause: invalid attempt to unpause functionality");
_paused = newPausedStatus;
emit Paused(msg.sender, newPausedStatus);
}
/**
* @notice Alias for `pause(type(uint256).max)`.
*/
function pauseAll() external onlyPauser {
_paused = type(uint256).max;
emit Paused(msg.sender, type(uint256).max);
}
/**
* @notice This function is used to unpause an EigenLayer contract's functionality.
* It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
*/
function unpause(uint256 newPausedStatus) external onlyUnpauser {
// verify that the `newPausedStatus` does not *flip* any bits (i.e. doesn't pause anything, all 0 bits remain)
require(
((~_paused) & (~newPausedStatus)) == (~_paused),
"Pausable.unpause: invalid attempt to pause functionality"
);
_paused = newPausedStatus;
emit Unpaused(msg.sender, newPausedStatus);
}
/// @notice Returns the current paused status as a uint256.
function paused() public view virtual returns (uint256) {
return _paused;
}
/// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
function paused(uint8 index) public view virtual returns (bool) {
uint256 mask = 1 << index;
return ((_paused & mask) == mask);
}
/// @notice Allows the unpauser to set a new pauser registry
function setPauserRegistry(IPauserRegistry newPauserRegistry) external onlyUnpauser {
_setPauserRegistry(newPauserRegistry);
}
/// internal function for setting pauser registry
function _setPauserRegistry(IPauserRegistry newPauserRegistry) internal {
require(
address(newPauserRegistry) != address(0),
"Pausable._setPauserRegistry: newPauserRegistry cannot be the zero address"
);
emit PauserRegistrySet(pauserRegistry, newPauserRegistry);
pauserRegistry = newPauserRegistry;
}
/**
* @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[48] private __gap;
}
RegistryCoordinatorStorage.sol 82 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {
/*******************************************************************************
CONSTANTS AND IMMUTABLES
*******************************************************************************/
/// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract
bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH =
keccak256("OperatorChurnApproval(address registeringOperator,bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams,bytes32 salt,uint256 expiry)OperatorKickParam(uint8 quorumNumber,address operator)");
/// @notice The EIP-712 typehash used for registering BLS public keys
bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)");
/// @notice The maximum value of a quorum bitmap
uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max;
/// @notice The basis point denominator
uint16 internal constant BIPS_DENOMINATOR = 10000;
/// @notice Index for flag that pauses operator registration
uint8 internal constant PAUSED_REGISTER_OPERATOR = 0;
/// @notice Index for flag that pauses operator deregistration
uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1;
/// @notice Index for flag pausing operator stake updates
uint8 internal constant PAUSED_UPDATE_OPERATOR = 2;
/// @notice The maximum number of quorums this contract supports
uint8 internal constant MAX_QUORUM_COUNT = 192;
/// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts
IServiceManager public immutable serviceManager;
/// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' aggregate BLS public keys per quorum
IBLSApkRegistry public immutable blsApkRegistry;
/// @notice the Stake Registry contract that will keep track of operators' stakes
IStakeRegistry public immutable stakeRegistry;
/// @notice the Index Registry contract that will keep track of operators' indexes
IIndexRegistry public immutable indexRegistry;
/*******************************************************************************
STATE
*******************************************************************************/
/// @notice the current number of quorums supported by the registry coordinator
uint8 public quorumCount;
/// @notice maps quorum number => operator cap and kick params
mapping(uint8 => OperatorSetParam) internal _quorumParams;
/// @notice maps operator id => historical quorums they registered for
mapping(bytes32 => QuorumBitmapUpdate[]) internal _operatorBitmapHistory;
/// @notice maps operator address => operator id and status
mapping(address => OperatorInfo) internal _operatorInfo;
/// @notice whether the salt has been used for an operator churn approval
mapping(bytes32 => bool) public isChurnApproverSaltUsed;
/// @notice mapping from quorum number to the latest block that all quorums were updated all at once
mapping(uint8 => uint256) public quorumUpdateBlockNumber;
/// @notice the dynamic-length array of the registries this coordinator is coordinating
address[] public registries;
/// @notice the address of the entity allowed to sign off on operators getting kicked out of the AVS during registration
address public churnApprover;
/// @notice the address of the entity allowed to eject operators from the AVS
address public ejector;
constructor(
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
) {
serviceManager = _serviceManager;
stakeRegistry = _stakeRegistry;
blsApkRegistry = _blsApkRegistry;
indexRegistry = _indexRegistry;
}
// storage gap for upgradeability
// slither-disable-next-line shadowing-state
uint256[41] private __GAP;
}
IRegistry.sol 13 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Minimal interface for a `Registry`-type contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Functions related to the registration process itself have been intentionally excluded
* because their function signatures may vary significantly.
*/
interface IRegistry {
function registryCoordinator() external view returns (address);
}
IDelegationManager.sol 469 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategy.sol";
import "./ISignatureUtils.sol";
import "./IStrategyManager.sol";
/**
* @title DelegationManager
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice This is the contract for delegation in EigenLayer. The main functionalities of this contract are
* - enabling anyone to register as an operator in EigenLayer
* - allowing operators to specify parameters related to stakers who delegate to them
* - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
* - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
*/
interface IDelegationManager is ISignatureUtils {
// @notice Struct used for storing information about a single operator who has registered with EigenLayer
struct OperatorDetails {
// @notice address to receive the rewards that the operator earns via serving applications built on EigenLayer.
address earningsReceiver;
/**
* @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations".
* @dev Signature verification follows these rules:
* 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed.
* 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator.
* 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
*/
address delegationApprover;
/**
* @notice A minimum delay -- measured in blocks -- enforced between:
* 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing`
* and
* 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate`
* @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails,
* then they are only allowed to either increase this value or keep it the same.
*/
uint32 stakerOptOutWindowBlocks;
}
/**
* @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator.
* @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function.
*/
struct StakerDelegation {
// the staker who is delegating
address staker;
// the operator being delegated to
address operator;
// the staker's nonce
uint256 nonce;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator.
* @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function.
*/
struct DelegationApproval {
// the staker who is delegating
address staker;
// the operator being delegated to
address operator;
// the operator's provided salt
bytes32 salt;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored.
* In functions that operate on existing queued withdrawals -- e.g. completeQueuedWithdrawal`, the data is resubmitted and the hash of the submitted
* data is computed by `calculateWithdrawalRoot` and checked against the stored hash in order to confirm the integrity of the submitted data.
*/
struct Withdrawal {
// The address that originated the Withdrawal
address staker;
// The address that the staker was delegated to at the time that the Withdrawal was created
address delegatedTo;
// The address that can complete the Withdrawal + will receive funds when completing the withdrawal
address withdrawer;
// Nonce used to guarantee that otherwise identical withdrawals have unique hashes
uint256 nonce;
// Block number when the Withdrawal was created
uint32 startBlock;
// Array of strategies that the Withdrawal contains
IStrategy[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
}
struct QueuedWithdrawalParams {
// Array of strategies that the QueuedWithdrawal contains
IStrategy[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
// The address of the withdrawer
address withdrawer;
}
// @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails.
event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails);
/// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails
event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails);
/**
* @notice Emitted when @param operator indicates that they are updating their MetadataURI string
* @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
*/
event OperatorMetadataURIUpdated(address indexed operator, string metadataURI);
/// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares.
event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);
/// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares.
event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);
/// @notice Emitted when @param staker delegates to @param operator.
event StakerDelegated(address indexed staker, address indexed operator);
/// @notice Emitted when @param staker undelegates from @param operator.
event StakerUndelegated(address indexed staker, address indexed operator);
/// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself
event StakerForceUndelegated(address indexed staker, address indexed operator);
/**
* @notice Emitted when a new withdrawal is queued.
* @param withdrawalRoot Is the hash of the `withdrawal`.
* @param withdrawal Is the withdrawal itself.
*/
event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal);
/// @notice Emitted when a queued withdrawal is completed
event WithdrawalCompleted(bytes32 withdrawalRoot);
/// @notice Emitted when a queued withdrawal is *migrated* from the StrategyManager to the DelegationManager
event WithdrawalMigrated(bytes32 oldWithdrawalRoot, bytes32 newWithdrawalRoot);
/// @notice Emitted when the `minWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
event MinWithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
/// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
event StrategyWithdrawalDelayBlocksSet(IStrategy strategy, uint256 previousValue, uint256 newValue);
/**
* @notice Registers the caller as an operator in EigenLayer.
* @param registeringOperatorDetails is the `OperatorDetails` for the operator.
* @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
*
* @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function registerAsOperator(
OperatorDetails calldata registeringOperatorDetails,
string calldata metadataURI
) external;
/**
* @notice Updates an operator's stored `OperatorDetails`.
* @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`.
*
* @dev The caller must have previously registered as an operator in EigenLayer.
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
*/
function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external;
/**
* @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
* @param metadataURI The URI for metadata associated with an operator
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function updateOperatorMetadataURI(string calldata metadataURI) external;
/**
* @notice Caller delegates their stake to an operator.
* @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer.
* @param approverSignatureAndExpiry Verifies the operator approves of this delegation
* @param approverSalt A unique single use value tied to an individual signature.
* @dev The approverSignatureAndExpiry is used in the event that:
* 1) the operator's `delegationApprover` address is set to a non-zero value.
* AND
* 2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator
* or their delegationApprover is the `msg.sender`, then approval is assumed.
* @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateTo(
address operator,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external;
/**
* @notice Caller delegates a staker's stake to an operator with valid signatures from both parties.
* @param staker The account delegating stake to an `operator` account
* @param operator The account (`staker`) is delegating its assets to for use in serving applications built on EigenLayer.
* @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator
* @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that:
* @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
*
* @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action.
* @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271.
* @dev the operator's `delegationApprover` address is set to a non-zero value.
* @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover
* is the `msg.sender`, then approval is assumed.
* @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
* @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateToBySignature(
address staker,
address operator,
SignatureWithExpiry memory stakerSignatureAndExpiry,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external;
/**
* @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager
* and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary.
* @param staker The account to be undelegated.
* @return withdrawalRoot The root of the newly queued withdrawal, if a withdrawal was queued. Otherwise just bytes32(0).
*
* @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves.
* @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover"
* @dev Reverts if the `staker` is already undelegated.
*/
function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot);
/**
* Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed
* from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from
* their operator.
*
* All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay.
*/
function queueWithdrawals(
QueuedWithdrawalParams[] calldata queuedWithdrawalParams
) external returns (bytes32[] memory);
/**
* @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer`
* @param withdrawal The Withdrawal to complete.
* @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array.
* This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused)
* @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array
* @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves
* and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies
* will simply be transferred to the caller directly.
* @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw`
* @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that
* any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in
* any other strategies, which will be transferred to the withdrawer.
*/
function completeQueuedWithdrawal(
Withdrawal calldata withdrawal,
IERC20[] calldata tokens,
uint256 middlewareTimesIndex,
bool receiveAsTokens
) external;
/**
* @notice Array-ified version of `completeQueuedWithdrawal`.
* Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer`
* @param withdrawals The Withdrawals to complete.
* @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
* @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
* @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
* @dev See `completeQueuedWithdrawal` for relevant dev tags
*/
function completeQueuedWithdrawals(
Withdrawal[] calldata withdrawals,
IERC20[][] calldata tokens,
uint256[] calldata middlewareTimesIndexes,
bool[] calldata receiveAsTokens
) external;
/**
* @notice Increases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to increase the delegated shares.
* @param shares The number of shares to increase.
*
* @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function increaseDelegatedShares(
address staker,
IStrategy strategy,
uint256 shares
) external;
/**
* @notice Decreases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to decrease the delegated shares.
* @param shares The number of shares to decrease.
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function decreaseDelegatedShares(
address staker,
IStrategy strategy,
uint256 shares
) external;
/**
* @notice returns the address of the operator that `staker` is delegated to.
* @notice Mapping: staker => operator whom the staker is currently delegated to.
* @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
*/
function delegatedTo(address staker) external view returns (address);
/**
* @notice Returns the OperatorDetails struct associated with an `operator`.
*/
function operatorDetails(address operator) external view returns (OperatorDetails memory);
/*
* @notice Returns the earnings receiver address for an operator
*/
function earningsReceiver(address operator) external view returns (address);
/**
* @notice Returns the delegationApprover account for an operator
*/
function delegationApprover(address operator) external view returns (address);
/**
* @notice Returns the stakerOptOutWindowBlocks for an operator
*/
function stakerOptOutWindowBlocks(address operator) external view returns (uint256);
/**
* @notice Given array of strategies, returns array of shares for the operator
*/
function getOperatorShares(
address operator,
IStrategy[] memory strategies
) external view returns (uint256[] memory);
/**
* @notice Given a list of strategies, return the minimum number of blocks that must pass to withdraw
* from all the inputted strategies. Return value is >= minWithdrawalDelayBlocks as this is the global min withdrawal delay.
* @param strategies The strategies to check withdrawal delays for
*/
function getWithdrawalDelay(IStrategy[] calldata strategies) external view returns (uint256);
/**
* @notice returns the total number of shares in `strategy` that are delegated to `operator`.
* @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator.
* @dev By design, the following invariant should hold for each Strategy:
* (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator)
* = sum (delegateable shares of all stakers delegated to the operator)
*/
function operatorShares(address operator, IStrategy strategy) external view returns (uint256);
/**
* @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
*/
function isDelegated(address staker) external view returns (bool);
/**
* @notice Returns true is an operator has previously registered for delegation.
*/
function isOperator(address operator) external view returns (bool);
/// @notice Mapping: staker => number of signed delegation nonces (used in `delegateToBySignature`) from the staker that the contract has already checked
function stakerNonce(address staker) external view returns (uint256);
/**
* @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
* @dev Salts are used in the `delegateTo` and `delegateToBySignature` functions. Note that these functions only process the delegationApprover's
* signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
*/
function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool);
/**
* @notice Minimum delay enforced by this contract for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
* Note that strategies each have a separate withdrawal delay, which can be greater than this value. So the minimum number of blocks that must pass
* to withdraw a strategy is MAX(minWithdrawalDelayBlocks, strategyWithdrawalDelayBlocks[strategy])
*/
function minWithdrawalDelayBlocks() external view returns (uint256);
/**
* @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
*/
function strategyWithdrawalDelayBlocks(IStrategy strategy) external view returns (uint256);
/// @notice return address of the beaconChainETHStrategy
function beaconChainETHStrategy() external view returns (IStrategy);
/**
* @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator`
* @param staker The signing staker
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateCurrentStakerDelegationDigestHash(
address staker,
address operator,
uint256 expiry
) external view returns (bytes32);
/**
* @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function
* @param staker The signing staker
* @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]`
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateStakerDelegationDigestHash(
address staker,
uint256 _stakerNonce,
address operator,
uint256 expiry
) external view returns (bytes32);
/**
* @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions.
* @param staker The account delegating their stake
* @param operator The account receiving delegated stake
* @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
* @param approverSalt A unique and single use value associated with the approver signature.
* @param expiry Time after which the approver's signature becomes invalid
*/
function calculateDelegationApprovalDigestHash(
address staker,
address operator,
address _delegationApprover,
bytes32 approverSalt,
uint256 expiry
) external view returns (bytes32);
/// @notice The EIP-712 typehash for the contract's domain
function DOMAIN_TYPEHASH() external view returns (bytes32);
/// @notice The EIP-712 typehash for the StakerDelegation struct used by the contract
function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32);
/// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
/**
* @notice Getter function for the current EIP-712 domain separator for this contract.
*
* @dev The domain separator will change in the event of a fork that changes the ChainID.
* @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision.
* for more detailed information please read EIP-712.
*/
function domainSeparator() external view returns (bytes32);
/// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
/// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
function cumulativeWithdrawalsQueued(address staker) external view returns (uint256);
/// @notice Returns the keccak256 hash of `withdrawal`.
function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32);
function migrateQueuedWithdrawals(IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue) external;
}
IStrategy.sol 95 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Minimal interface for an `Strategy` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Custom `Strategy` implementations may expand extensively on this interface.
*/
interface IStrategy {
/**
* @notice Used to deposit tokens into this Strategy
* @param token is the ERC20 token being deposited
* @param amount is the amount of token being deposited
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well.
* @return newShares is the number of new shares issued at the current exchange ratio.
*/
function deposit(IERC20 token, uint256 amount) external returns (uint256);
/**
* @notice Used to withdraw tokens from this Strategy, to the `recipient`'s address
* @param recipient is the address to receive the withdrawn funds
* @param token is the ERC20 token being transferred out
* @param amountShares is the amount of shares being withdrawn
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* other functions, and individual share balances are recorded in the strategyManager as well.
*/
function withdraw(address recipient, IERC20 token, uint256 amountShares) external;
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlying(uint256 amountShares) external returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToShares(uint256 amountUnderlying) external returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications
*/
function userUnderlying(address user) external returns (uint256);
/**
* @notice convenience function for fetching the current total shares of `user` in this strategy, by
* querying the `strategyManager` contract
*/
function shares(address user) external view returns (uint256);
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlyingView(uint256 amountShares) external view returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToSharesView(uint256 amountUnderlying) external view returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
*/
function userUnderlyingView(address user) external view returns (uint256);
/// @notice The underlying token for shares in this Strategy
function underlyingToken() external view returns (IERC20);
/// @notice The total number of extant shares in this Strategy
function totalShares() external view returns (uint256);
/// @notice Returns either a brief string explaining the strategy's goal & purpose, or a link to metadata that explains in more detail.
function explanation() external view returns (string memory);
}
IERC1271.sol 19 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
Address.sol 222 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
ECDSA.sol 232 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
ContextUpgradeable.sol 37 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @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;
}
AddressUpgradeable.sol 195 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
IPausable.sol 65 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "../interfaces/IPauserRegistry.sol";
/**
* @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
* These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
* @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
* Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
* For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
* you can only flip (any number of) switches to off/0 (aka "paused").
* If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
* 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
* 2) update the paused state to this new value
* @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
* indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
*/
interface IPausable {
/// @notice Emitted when the `pauserRegistry` is set to `newPauserRegistry`.
event PauserRegistrySet(IPauserRegistry pauserRegistry, IPauserRegistry newPauserRegistry);
/// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
event Paused(address indexed account, uint256 newPausedStatus);
/// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
event Unpaused(address indexed account, uint256 newPausedStatus);
/// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
function pauserRegistry() external view returns (IPauserRegistry);
/**
* @notice This function is used to pause an EigenLayer contract's functionality.
* It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
*/
function pause(uint256 newPausedStatus) external;
/**
* @notice Alias for `pause(type(uint256).max)`.
*/
function pauseAll() external;
/**
* @notice This function is used to unpause an EigenLayer contract's functionality.
* It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
*/
function unpause(uint256 newPausedStatus) external;
/// @notice Returns the current paused status as a uint256.
function paused() external view returns (uint256);
/// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
function paused(uint8 index) external view returns (bool);
/// @notice Allows the unpauser to set a new pauser registry
function setPauserRegistry(IPauserRegistry newPauserRegistry) external;
}
IStrategyManager.sol 164 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategy.sol";
import "./ISlasher.sol";
import "./IDelegationManager.sol";
import "./IEigenPodManager.sol";
/**
* @title Interface for the primary entrypoint for funds into EigenLayer.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice See the `StrategyManager` contract itself for implementation details.
*/
interface IStrategyManager {
/**
* @notice Emitted when a new deposit occurs on behalf of `staker`.
* @param staker Is the staker who is depositing funds into EigenLayer.
* @param strategy Is the strategy that `staker` has deposited into.
* @param token Is the token that `staker` deposited.
* @param shares Is the number of new shares `staker` has been granted in `strategy`.
*/
event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares);
/// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner
event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value);
/// @notice Emitted when the `strategyWhitelister` is changed
event StrategyWhitelisterChanged(address previousAddress, address newAddress);
/// @notice Emitted when a strategy is added to the approved list of strategies for deposit
event StrategyAddedToDepositWhitelist(IStrategy strategy);
/// @notice Emitted when a strategy is removed from the approved list of strategies for deposit
event StrategyRemovedFromDepositWhitelist(IStrategy strategy);
/**
* @notice Deposits `amount` of `token` into the specified `strategy`, with the resultant shares credited to `msg.sender`
* @param strategy is the specified strategy where deposit is to be made,
* @param token is the denomination in which the deposit is to be made,
* @param amount is the amount of token to be deposited in the strategy by the staker
* @return shares The amount of new shares in the `strategy` created as part of the action.
* @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
* @dev Cannot be called by an address that is 'frozen' (this function will revert if the `msg.sender` is frozen).
*
* WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors
* where the token balance and corresponding strategy shares are not in sync upon reentrancy.
*/
function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares);
/**
* @notice Used for depositing an asset into the specified strategy with the resultant shares credited to `staker`,
* who must sign off on the action.
* Note that the assets are transferred out/from the `msg.sender`, not from the `staker`; this function is explicitly designed
* purely to help one address deposit 'for' another.
* @param strategy is the specified strategy where deposit is to be made,
* @param token is the denomination in which the deposit is to be made,
* @param amount is the amount of token to be deposited in the strategy by the staker
* @param staker the staker that the deposited assets will be credited to
* @param expiry the timestamp at which the signature expires
* @param signature is a valid signature from the `staker`. either an ECDSA signature if the `staker` is an EOA, or data to forward
* following EIP-1271 if the `staker` is a contract
* @return shares The amount of new shares in the `strategy` created as part of the action.
* @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
* @dev A signature is required for this function to eliminate the possibility of griefing attacks, specifically those
* targeting stakers who may be attempting to undelegate.
* @dev Cannot be called if thirdPartyTransfersForbidden is set to true for this strategy
*
* WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors
* where the token balance and corresponding strategy shares are not in sync upon reentrancy
*/
function depositIntoStrategyWithSignature(
IStrategy strategy,
IERC20 token,
uint256 amount,
address staker,
uint256 expiry,
bytes memory signature
) external returns (uint256 shares);
/// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue
function removeShares(address staker, IStrategy strategy, uint256 shares) external;
/// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external;
/// @notice Used by the DelegationManager to convert withdrawn shares to tokens and send them to a recipient
function withdrawSharesAsTokens(address recipient, IStrategy strategy, uint256 shares, IERC20 token) external;
/// @notice Returns the current shares of `user` in `strategy`
function stakerStrategyShares(address user, IStrategy strategy) external view returns (uint256 shares);
/**
* @notice Get all details on the staker's deposits and corresponding shares
* @return (staker's strategies, shares in these strategies)
*/
function getDeposits(address staker) external view returns (IStrategy[] memory, uint256[] memory);
/// @notice Simple getter function that returns `stakerStrategyList[staker].length`.
function stakerStrategyListLength(address staker) external view returns (uint256);
/**
* @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into
* @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already)
* @param thirdPartyTransfersForbiddenValues bool values to set `thirdPartyTransfersForbidden` to for each strategy
*/
function addStrategiesToDepositWhitelist(
IStrategy[] calldata strategiesToWhitelist,
bool[] calldata thirdPartyTransfersForbiddenValues
) external;
/**
* @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into
* @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it)
*/
function removeStrategiesFromDepositWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external;
/// @notice Returns the single, central Delegation contract of EigenLayer
function delegation() external view returns (IDelegationManager);
/// @notice Returns the single, central Slasher contract of EigenLayer
function slasher() external view returns (ISlasher);
/// @notice Returns the EigenPodManager contract of EigenLayer
function eigenPodManager() external view returns (IEigenPodManager);
/// @notice Returns the address of the `strategyWhitelister`
function strategyWhitelister() external view returns (address);
/// @notice Returns bool for whether or not `strategy` is whitelisted for deposit
function strategyIsWhitelistedForDeposit(IStrategy strategy) external view returns (bool);
/**
* @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling
* depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker.
*/
function thirdPartyTransfersForbidden(IStrategy strategy) external view returns (bool);
// LIMITED BACKWARDS-COMPATIBILITY FOR DEPRECATED FUNCTIONALITY
// packed struct for queued withdrawals; helps deal with stack-too-deep errors
struct DeprecatedStruct_WithdrawerAndNonce {
address withdrawer;
uint96 nonce;
}
/**
* Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored.
* In functions that operate on existing queued withdrawals -- e.g. `startQueuedWithdrawalWaitingPeriod` or `completeQueuedWithdrawal`,
* the data is resubmitted and the hash of the submitted data is computed by `calculateWithdrawalRoot` and checked against the
* stored hash in order to confirm the integrity of the submitted data.
*/
struct DeprecatedStruct_QueuedWithdrawal {
IStrategy[] strategies;
uint256[] shares;
address staker;
DeprecatedStruct_WithdrawerAndNonce withdrawerAndNonce;
uint32 withdrawalStartBlock;
address delegatedAddress;
}
function migrateQueuedWithdrawal(DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal) external returns (bool, bytes32);
function calculateWithdrawalRoot(DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal) external pure returns (bytes32);
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
Strings.sol 75 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
ISlasher.sol 195 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategyManager.sol";
import "./IDelegationManager.sol";
/**
* @title Interface for the primary 'slashing' contract for EigenLayer.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice See the `Slasher` contract itself for implementation details.
*/
interface ISlasher {
// struct used to store information about the current state of an operator's obligations to middlewares they are serving
struct MiddlewareTimes {
// The update block for the middleware whose most recent update was earliest, i.e. the 'stalest' update out of all middlewares the operator is serving
uint32 stalestUpdateBlock;
// The latest 'serveUntilBlock' from all of the middleware that the operator is serving
uint32 latestServeUntilBlock;
}
// struct used to store details relevant to a single middleware that an operator has opted-in to serving
struct MiddlewareDetails {
// the block at which the contract begins being able to finalize the operator's registration with the service via calling `recordFirstStakeUpdate`
uint32 registrationMayBeginAtBlock;
// the block before which the contract is allowed to slash the user
uint32 contractCanSlashOperatorUntilBlock;
// the block at which the middleware's view of the operator's stake was most recently updated
uint32 latestUpdateBlock;
}
/// @notice Emitted when a middleware times is added to `operator`'s array.
event MiddlewareTimesAdded(
address operator,
uint256 index,
uint32 stalestUpdateBlock,
uint32 latestServeUntilBlock
);
/// @notice Emitted when `operator` begins to allow `contractAddress` to slash them.
event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
/// @notice Emitted when `contractAddress` signals that it will no longer be able to slash `operator` after the `contractCanSlashOperatorUntilBlock`.
event SlashingAbilityRevoked(
address indexed operator,
address indexed contractAddress,
uint32 contractCanSlashOperatorUntilBlock
);
/**
* @notice Emitted when `slashingContract` 'freezes' the `slashedOperator`.
* @dev The `slashingContract` must have permission to slash the `slashedOperator`, i.e. `canSlash(slasherOperator, slashingContract)` must return 'true'.
*/
event OperatorFrozen(address indexed slashedOperator, address indexed slashingContract);
/// @notice Emitted when `previouslySlashedAddress` is 'unfrozen', allowing them to again move deposited funds within EigenLayer.
event FrozenStatusReset(address indexed previouslySlashedAddress);
/**
* @notice Gives the `contractAddress` permission to slash the funds of the caller.
* @dev Typically, this function must be called prior to registering for a middleware.
*/
function optIntoSlashing(address contractAddress) external;
/**
* @notice Used for 'slashing' a certain operator.
* @param toBeFrozen The operator to be frozen.
* @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop.
* @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`.
*/
function freezeOperator(address toBeFrozen) external;
/**
* @notice Removes the 'frozen' status from each of the `frozenAddresses`
* @dev Callable only by the contract owner (i.e. governance).
*/
function resetFrozenStatus(address[] calldata frozenAddresses) external;
/**
* @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration
* is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param serveUntilBlock the block until which the operator's stake at the current block is slashable
* @dev adds the middleware's slashing contract to the operator's linked list
*/
function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) external;
/**
* @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals)
* to make sure the operator's stake at updateBlock is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param updateBlock the block for which the stake update is being recorded
* @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable
* @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after
* @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions,
* but it is anticipated to be rare and not detrimental.
*/
function recordStakeUpdate(
address operator,
uint32 updateBlock,
uint32 serveUntilBlock,
uint256 insertAfter
) external;
/**
* @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration
* is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param serveUntilBlock the block until which the operator's stake at the current block is slashable
* @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to
* slash `operator` once `serveUntil` is reached
*/
function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilBlock) external;
/// @notice The StrategyManager contract of EigenLayer
function strategyManager() external view returns (IStrategyManager);
/// @notice The DelegationManager contract of EigenLayer
function delegation() external view returns (IDelegationManager);
/**
* @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to
* slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed
* and the staker's status is reset (to 'unfrozen').
* @param staker The staker of interest.
* @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated
* to an operator who has their status set to frozen. Otherwise returns 'false'.
*/
function isFrozen(address staker) external view returns (bool);
/// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
function canSlash(address toBeSlashed, address slashingContract) external view returns (bool);
/// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`.
function contractCanSlashOperatorUntilBlock(
address operator,
address serviceContract
) external view returns (uint32);
/// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake
function latestUpdateBlock(address operator, address serviceContract) external view returns (uint32);
/// @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`.
function getCorrectValueForInsertAfter(address operator, uint32 updateBlock) external view returns (uint256);
/**
* @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used
* to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `operatorToMiddlewareTimes[operator]`). The specified
* struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal.
* This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event
* that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist.
* @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator,
* this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`.
* @param withdrawalStartBlock The block number at which the withdrawal was initiated.
* @param middlewareTimesIndex Indicates an index in `operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw
* @dev The correct `middlewareTimesIndex` input should be computable off-chain.
*/
function canWithdraw(
address operator,
uint32 withdrawalStartBlock,
uint256 middlewareTimesIndex
) external returns (bool);
/**
* operator =>
* [
* (
* the least recent update block of all of the middlewares it's serving/served,
* latest time that the stake bonded at that update needed to serve until
* )
* ]
*/
function operatorToMiddlewareTimes(
address operator,
uint256 arrayIndex
) external view returns (MiddlewareTimes memory);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator].length`
function middlewareTimesLength(address operator) external view returns (uint256);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`.
function getMiddlewareTimesIndexStalestUpdateBlock(address operator, uint32 index) external view returns (uint32);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
function getMiddlewareTimesIndexServeUntilBlock(address operator, uint32 index) external view returns (uint32);
/// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256);
/// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`).
function operatorWhitelistedContractsLinkedListEntry(
address operator,
address node
) external view returns (bool, uint256, uint256);
}
IEigenPodManager.sol 159 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import "./IETHPOSDeposit.sol";
import "./IStrategyManager.sol";
import "./IEigenPod.sol";
import "./IBeaconChainOracle.sol";
import "./IPausable.sol";
import "./ISlasher.sol";
import "./IStrategy.sol";
/**
* @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IEigenPodManager is IPausable {
/// @notice Emitted to notify the update of the beaconChainOracle address
event BeaconOracleUpdated(address indexed newOracleAddress);
/// @notice Emitted to notify the deployment of an EigenPod
event PodDeployed(address indexed eigenPod, address indexed podOwner);
/// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager
event BeaconChainETHDeposited(address indexed podOwner, uint256 amount);
/// @notice Emitted when the balance of an EigenPod is updated
event PodSharesUpdated(address indexed podOwner, int256 sharesDelta);
/// @notice Emitted when a withdrawal of beacon chain ETH is completed
event BeaconChainETHWithdrawalCompleted(
address indexed podOwner,
uint256 shares,
uint96 nonce,
address delegatedAddress,
address withdrawer,
bytes32 withdrawalRoot
);
event DenebForkTimestampUpdated(uint64 newValue);
/**
* @notice Creates an EigenPod for the sender.
* @dev Function will revert if the `msg.sender` already has an EigenPod.
* @dev Returns EigenPod address
*/
function createPod() external returns (address);
/**
* @notice Stakes for a new beacon chain validator on the sender's EigenPod.
* Also creates an EigenPod for the sender if they don't have one already.
* @param pubkey The 48 bytes public key of the beacon chain validator.
* @param signature The validator's signature of the deposit data.
* @param depositDataRoot The root/hash of the deposit data for the validator's deposit.
*/
function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;
/**
* @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager
* to ensure that delegated shares are also tracked correctly
* @param podOwner is the pod owner whose balance is being updated.
* @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares
* @dev Callable only by the podOwner's EigenPod contract.
* @dev Reverts if `sharesDelta` is not a whole Gwei amount
*/
function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external;
/**
* @notice Updates the oracle contract that provides the beacon chain state root
* @param newBeaconChainOracle is the new oracle contract being pointed to
* @dev Callable only by the owner of this contract (i.e. governance)
*/
function updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) external;
/// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
function ownerToPod(address podOwner) external view returns (IEigenPod);
/// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
function getPod(address podOwner) external view returns (IEigenPod);
/// @notice The ETH2 Deposit Contract
function ethPOS() external view returns (IETHPOSDeposit);
/// @notice Beacon proxy to which the EigenPods point
function eigenPodBeacon() external view returns (IBeacon);
/// @notice Oracle contract that provides updates to the beacon chain's state
function beaconChainOracle() external view returns (IBeaconChainOracle);
/// @notice Returns the beacon block root at `timestamp`. Reverts if the Beacon block root at `timestamp` has not yet been finalized.
function getBlockRootAtTimestamp(uint64 timestamp) external view returns (bytes32);
/// @notice EigenLayer's StrategyManager contract
function strategyManager() external view returns (IStrategyManager);
/// @notice EigenLayer's Slasher contract
function slasher() external view returns (ISlasher);
/// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
function hasPod(address podOwner) external view returns (bool);
/// @notice Returns the number of EigenPods that have been created
function numPods() external view returns (uint256);
/**
* @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy.
* @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can
* decrease between the pod owner queuing and completing a withdrawal.
* When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_.
* Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this
* as the withdrawal "paying off the deficit".
*/
function podOwnerShares(address podOwner) external view returns (int256);
/// @notice returns canonical, virtual beaconChainETH strategy
function beaconChainETHStrategy() external view returns (IStrategy);
/**
* @notice Used by the DelegationManager to remove a pod owner's shares while they're in the withdrawal queue.
* Simply decreases the `podOwner`'s shares by `shares`, down to a minimum of zero.
* @dev This function reverts if it would result in `podOwnerShares[podOwner]` being less than zero, i.e. it is forbidden for this function to
* result in the `podOwner` incurring a "share deficit". This behavior prevents a Staker from queuing a withdrawal which improperly removes excessive
* shares from the operator to whom the staker is delegated.
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function removeShares(address podOwner, uint256 shares) external;
/**
* @notice Increases the `podOwner`'s shares by `shares`, paying off deficit if possible.
* Used by the DelegationManager to award a pod owner shares on exiting the withdrawal queue
* @dev Returns the number of shares added to `podOwnerShares[podOwner]` above zero, which will be less than the `shares` input
* in the event that the podOwner has an existing shares deficit (i.e. `podOwnerShares[podOwner]` starts below zero)
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function addShares(address podOwner, uint256 shares) external returns (uint256);
/**
* @notice Used by the DelegationManager to complete a withdrawal, sending tokens to some destination address
* @dev Prioritizes decreasing the podOwner's share deficit, if they have one
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external;
/**
* @notice the deneb hard fork timestamp used to determine which proof path to use for proving a withdrawal
*/
function denebForkTimestamp() external view returns (uint64);
/**
* setting the deneb hard fork timestamp by the eigenPodManager owner
* @dev this function is designed to be called twice. Once, it is set to type(uint64).max
* prior to the actual deneb fork timestamp being set, and then the second time it is set
* to the actual deneb fork timestamp.
*/
function setDenebForkTimestamp(uint64 newDenebForkTimestamp) external;
}
IBeacon.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @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.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
IETHPOSDeposit.sol 41 lines
// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.5.0;
// This interface is designed to be compatible with the Vyper version.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
interface IETHPOSDeposit {
/// @notice A processed deposit event.
event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index);
/// @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;
/// @notice Query the current deposit root hash.
/// @return The deposit root hash.
function get_deposit_root() external view returns (bytes32);
/// @notice Query the current deposit count.
/// @return The deposit count encoded as a little endian 64-bit number.
function get_deposit_count() external view returns (bytes memory);
}
IEigenPod.sol 223 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "../libraries/BeaconChainProofs.sol";
import "./IEigenPodManager.sol";
import "./IBeaconChainOracle.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title The implementation contract used for restaking beacon chain ETH on EigenLayer
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice The main functionalities are:
* - creating new ETH validators with their withdrawal credentials pointed to this contract
* - proving from beacon chain state roots that withdrawal credentials are pointed to this contract
* - proving from beacon chain state roots the balances of ETH validators with their withdrawal credentials
* pointed to this contract
* - updating aggregate balances in the EigenPodManager
* - withdrawing eth when withdrawals are initiated
* @dev Note that all beacon chain balances are stored as gwei within the beacon chain datastructures. We choose
* to account balances in terms of gwei in the EigenPod contract and convert to wei when making calls to other contracts
*/
interface IEigenPod {
enum VALIDATOR_STATUS {
INACTIVE, // doesnt exist
ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod
WITHDRAWN // withdrawn from the Beacon Chain
}
struct ValidatorInfo {
// index of the validator in the beacon chain
uint64 validatorIndex;
// amount of beacon chain ETH restaked on EigenLayer in gwei
uint64 restakedBalanceGwei;
//timestamp of the validator's most recent balance update
uint64 mostRecentBalanceUpdateTimestamp;
// status of the validator
VALIDATOR_STATUS status;
}
/**
* @notice struct used to store amounts related to proven withdrawals in memory. Used to help
* manage stack depth and optimize the number of external calls, when batching withdrawal operations.
*/
struct VerifiedWithdrawal {
// amount to send to a podOwner from a proven withdrawal
uint256 amountToSendGwei;
// difference in shares to be recorded in the eigenPodManager, as a result of the withdrawal
int256 sharesDeltaGwei;
}
enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
REDEEMED,
PENDING,
FAILED
}
/// @notice Emitted when an ETH validator stakes via this eigenPod
event EigenPodStaked(bytes pubkey);
/// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod
event ValidatorRestaked(uint40 validatorIndex);
/// @notice Emitted when an ETH validator's balance is proven to be updated. Here newValidatorBalanceGwei
// is the validator's balance that is credited on EigenLayer.
event ValidatorBalanceUpdated(uint40 validatorIndex, uint64 balanceTimestamp, uint64 newValidatorBalanceGwei);
/// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain
event FullWithdrawalRedeemed(
uint40 validatorIndex,
uint64 withdrawalTimestamp,
address indexed recipient,
uint64 withdrawalAmountGwei
);
/// @notice Emitted when a partial withdrawal claim is successfully redeemed
event PartialWithdrawalRedeemed(
uint40 validatorIndex,
uint64 withdrawalTimestamp,
address indexed recipient,
uint64 partialWithdrawalAmountGwei
);
/// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);
/// @notice Emitted when podOwner enables restaking
event RestakingActivated(address indexed podOwner);
/// @notice Emitted when ETH is received via the `receive` fallback
event NonBeaconChainETHReceived(uint256 amountReceived);
/// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn
event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn);
/// @notice The max amount of eth, in gwei, that can be restaked per validator
function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);
/// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer),
function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);
/// @notice any ETH deposited into the EigenPod contract via the `receive` fallback function
function nonBeaconChainETHBalanceWei() external view returns (uint256);
/// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
function initialize(address owner) external;
/// @notice Called by EigenPodManager when the owner wants to create another ETH validator.
function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;
/**
* @notice Transfers `amountWei` in ether from this contract to the specified `recipient` address
* @notice Called by EigenPodManager to withdrawBeaconChainETH that has been added to the EigenPod's balance due to a withdrawal from the beacon chain.
* @dev The podOwner must have already proved sufficient withdrawals, so that this pod's `withdrawableRestakedExecutionLayerGwei` exceeds the
* `amountWei` input (when converted to GWEI).
* @dev Reverts if `amountWei` is not a whole Gwei amount
*/
function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external;
/// @notice The single EigenPodManager for EigenLayer
function eigenPodManager() external view returns (IEigenPodManager);
/// @notice The owner of this EigenPod
function podOwner() external view returns (address);
/// @notice an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`.
function hasRestaked() external view returns (bool);
/**
* @notice The latest timestamp at which the pod owner withdrew the balance of the pod, via calling `withdrawBeforeRestaking`.
* @dev This variable is only updated when the `withdrawBeforeRestaking` function is called, which can only occur before `hasRestaked` is set to true for this pod.
* Proofs for this pod are only valid against Beacon Chain state roots corresponding to timestamps after the stored `mostRecentWithdrawalTimestamp`.
*/
function mostRecentWithdrawalTimestamp() external view returns (uint64);
/// @notice Returns the validatorInfo struct for the provided pubkeyHash
function validatorPubkeyHashToInfo(bytes32 validatorPubkeyHash) external view returns (ValidatorInfo memory);
/// @notice Returns the validatorInfo struct for the provided pubkey
function validatorPubkeyToInfo(bytes calldata validatorPubkey) external view returns (ValidatorInfo memory);
///@notice mapping that tracks proven withdrawals
function provenWithdrawal(bytes32 validatorPubkeyHash, uint64 slot) external view returns (bool);
/// @notice This returns the status of a given validator
function validatorStatus(bytes32 pubkeyHash) external view returns (VALIDATOR_STATUS);
/// @notice This returns the status of a given validator pubkey
function validatorStatus(bytes calldata validatorPubkey) external view returns (VALIDATOR_STATUS);
/**
* @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to
* this contract. It also verifies the effective balance of the validator. It verifies the provided proof of the ETH validator against the beacon chain state
* root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer.
* @param oracleTimestamp is the Beacon Chain timestamp whose state root the `proof` will be proven against.
* @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
* @param withdrawalCredentialProofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials
* against a beacon chain state root
* @param validatorFields are the fields of the "Validator Container", refer to consensus specs
* for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
*/
function verifyWithdrawalCredentials(
uint64 oracleTimestamp,
BeaconChainProofs.StateRootProof calldata stateRootProof,
uint40[] calldata validatorIndices,
bytes[] calldata withdrawalCredentialProofs,
bytes32[][] calldata validatorFields
)
external;
/**
* @notice This function records an update (either increase or decrease) in the pod's balance in the StrategyManager.
It also verifies a merkle proof of the validator's current beacon chain balance.
* @param oracleTimestamp The oracleTimestamp whose state root the `proof` will be proven against.
* Must be within `VERIFY_BALANCE_UPDATE_WINDOW_SECONDS` of the current block.
* @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
* @param validatorFieldsProofs proofs against the `beaconStateRoot` for each validator in `validatorFields`
* @param validatorFields are the fields of the "Validator Container", refer to consensus specs
* @dev For more details on the Beacon Chain spec, see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
*/
function verifyBalanceUpdates(
uint64 oracleTimestamp,
uint40[] calldata validatorIndices,
BeaconChainProofs.StateRootProof calldata stateRootProof,
bytes[] calldata validatorFieldsProofs,
bytes32[][] calldata validatorFields
) external;
/**
* @notice This function records full and partial withdrawals on behalf of one of the Ethereum validators for this EigenPod
* @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against
* @param withdrawalProofs is the information needed to check the veracity of the block numbers and withdrawals being proven
* @param validatorFieldsProofs is the proof of the validator's fields' in the validator tree
* @param withdrawalFields are the fields of the withdrawals being proven
* @param validatorFields are the fields of the validators being proven
*/
function verifyAndProcessWithdrawals(
uint64 oracleTimestamp,
BeaconChainProofs.StateRootProof calldata stateRootProof,
BeaconChainProofs.WithdrawalProof[] calldata withdrawalProofs,
bytes[] calldata validatorFieldsProofs,
bytes32[][] calldata validatorFields,
bytes32[][] calldata withdrawalFields
) external;
/**
* @notice Called by the pod owner to activate restaking by withdrawing
* all existing ETH from the pod and preventing further withdrawals via
* "withdrawBeforeRestaking()"
*/
function activateRestaking() external;
/// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
function withdrawBeforeRestaking() external;
/// @notice Called by the pod owner to withdraw the nonBeaconChainETHBalanceWei
function withdrawNonBeaconChainETHBalanceWei(address recipient, uint256 amountToWithdraw) external;
/// @notice called by owner of a pod to remove any ERC20s deposited in the pod
function recoverTokens(IERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external;
}
IBeaconChainOracle.sol 12 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Interface for the BeaconStateOracle contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IBeaconChainOracle {
/// @notice The block number to state root mapping.
function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32);
}
BeaconChainProofs.sol 409 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "./Merkle.sol";
import "../libraries/Endian.sol";
//Utility library for parsing and PHASE0 beacon chain block headers
//SSZ Spec: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization
//BeaconBlockHeader Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
//BeaconState Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconstate
library BeaconChainProofs {
// constants are the number of fields and the heights of the different merkle trees used in merkleizing beacon chain containers
uint256 internal constant BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT = 3;
uint256 internal constant BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT = 4;
uint256 internal constant BEACON_STATE_FIELD_TREE_HEIGHT = 5;
uint256 internal constant VALIDATOR_FIELD_TREE_HEIGHT = 3;
//Note: changed in the deneb hard fork from 4->5
uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB = 5;
uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA = 4;
// SLOTS_PER_HISTORICAL_ROOT = 2**13, so tree height is 13
uint256 internal constant BLOCK_ROOTS_TREE_HEIGHT = 13;
//HISTORICAL_ROOTS_LIMIT = 2**24, so tree height is 24
uint256 internal constant HISTORICAL_SUMMARIES_TREE_HEIGHT = 24;
//Index of block_summary_root in historical_summary container
uint256 internal constant BLOCK_SUMMARY_ROOT_INDEX = 0;
// tree height for hash tree of an individual withdrawal container
uint256 internal constant WITHDRAWAL_FIELD_TREE_HEIGHT = 2;
uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;
// MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
//in beacon block body https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconblockbody
uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
// in beacon block header https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
uint256 internal constant SLOT_INDEX = 0;
uint256 internal constant STATE_ROOT_INDEX = 3;
uint256 internal constant BODY_ROOT_INDEX = 4;
// in beacon state https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate
uint256 internal constant VALIDATOR_TREE_ROOT_INDEX = 11;
uint256 internal constant HISTORICAL_SUMMARIES_INDEX = 27;
// in validator https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;
uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1;
uint256 internal constant VALIDATOR_BALANCE_INDEX = 2;
uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;
// in execution payload header
uint256 internal constant TIMESTAMP_INDEX = 9;
//in execution payload
uint256 internal constant WITHDRAWALS_INDEX = 14;
// in withdrawal
uint256 internal constant WITHDRAWAL_VALIDATOR_INDEX_INDEX = 1;
uint256 internal constant WITHDRAWAL_VALIDATOR_AMOUNT_INDEX = 3;
//Misc Constants
/// @notice The number of slots each epoch in the beacon chain
uint64 internal constant SLOTS_PER_EPOCH = 32;
/// @notice The number of seconds in a slot in the beacon chain
uint64 internal constant SECONDS_PER_SLOT = 12;
/// @notice Number of seconds per epoch: 384 == 32 slots/epoch * 12 seconds/slot
uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT;
bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
/// @notice This struct contains the merkle proofs and leaves needed to verify a partial/full withdrawal
struct WithdrawalProof {
bytes withdrawalProof;
bytes slotProof;
bytes executionPayloadProof;
bytes timestampProof;
bytes historicalSummaryBlockRootProof;
uint64 blockRootIndex;
uint64 historicalSummaryIndex;
uint64 withdrawalIndex;
bytes32 blockRoot;
bytes32 slotRoot;
bytes32 timestampRoot;
bytes32 executionPayloadRoot;
}
/// @notice This struct contains the root and proof for verifying the state root against the oracle block root
struct StateRootProof {
bytes32 beaconStateRoot;
bytes proof;
}
/**
* @notice This function verifies merkle proofs of the fields of a certain validator against a beacon chain state root
* @param validatorIndex the index of the proven validator
* @param beaconStateRoot is the beacon chain state root to be proven against.
* @param validatorFieldsProof is the data used in proving the validator's fields
* @param validatorFields the claimed fields of the validator
*/
function verifyValidatorFields(
bytes32 beaconStateRoot,
bytes32[] calldata validatorFields,
bytes calldata validatorFieldsProof,
uint40 validatorIndex
) internal view {
require(
validatorFields.length == 2 ** VALIDATOR_FIELD_TREE_HEIGHT,
"BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length"
);
/**
* Note: the length of the validator merkle proof is BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1.
* There is an additional layer added by hashing the root with the length of the validator list
*/
require(
validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyValidatorFields: Proof has incorrect length"
);
uint256 index = (VALIDATOR_TREE_ROOT_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex);
// merkleize the validatorFields to get the leaf to prove
bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields);
// verify the proof of the validatorRoot against the beaconStateRoot
require(
Merkle.verifyInclusionSha256({
proof: validatorFieldsProof,
root: beaconStateRoot,
leaf: validatorRoot,
index: index
}),
"BeaconChainProofs.verifyValidatorFields: Invalid merkle proof"
);
}
/**
* @notice This function verifies the latestBlockHeader against the state root. the latestBlockHeader is
* a tracked in the beacon state.
* @param beaconStateRoot is the beacon chain state root to be proven against.
* @param stateRootProof is the provided merkle proof
* @param latestBlockRoot is hashtree root of the latest block header in the beacon state
*/
function verifyStateRootAgainstLatestBlockRoot(
bytes32 latestBlockRoot,
bytes32 beaconStateRoot,
bytes calldata stateRootProof
) internal view {
require(
stateRootProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Proof has incorrect length"
);
//Next we verify the slot against the blockRoot
require(
Merkle.verifyInclusionSha256({
proof: stateRootProof,
root: latestBlockRoot,
leaf: beaconStateRoot,
index: STATE_ROOT_INDEX
}),
"BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Invalid latest block header root merkle proof"
);
}
/**
* @notice This function verifies the slot and the withdrawal fields for a given withdrawal
* @param withdrawalProof is the provided set of merkle proofs
* @param withdrawalFields is the serialized withdrawal container to be proven
*/
function verifyWithdrawal(
bytes32 beaconStateRoot,
bytes32[] calldata withdrawalFields,
WithdrawalProof calldata withdrawalProof,
uint64 denebForkTimestamp
) internal view {
require(
withdrawalFields.length == 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: withdrawalFields has incorrect length"
);
require(
withdrawalProof.blockRootIndex < 2 ** BLOCK_ROOTS_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: blockRootIndex is too large"
);
require(
withdrawalProof.withdrawalIndex < 2 ** WITHDRAWALS_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: withdrawalIndex is too large"
);
require(
withdrawalProof.historicalSummaryIndex < 2 ** HISTORICAL_SUMMARIES_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: historicalSummaryIndex is too large"
);
//Note: post deneb hard fork, the number of exection payload header fields increased from 15->17, adding an extra level to the tree height
uint256 executionPayloadHeaderFieldTreeHeight = (getWithdrawalTimestamp(withdrawalProof) < denebForkTimestamp) ? EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA : EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB;
require(
withdrawalProof.withdrawalProof.length ==
32 * (executionPayloadHeaderFieldTreeHeight + WITHDRAWALS_TREE_HEIGHT + 1),
"BeaconChainProofs.verifyWithdrawal: withdrawalProof has incorrect length"
);
require(
withdrawalProof.executionPayloadProof.length ==
32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyWithdrawal: executionPayloadProof has incorrect length"
);
require(
withdrawalProof.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyWithdrawal: slotProof has incorrect length"
);
require(
withdrawalProof.timestampProof.length == 32 * (executionPayloadHeaderFieldTreeHeight),
"BeaconChainProofs.verifyWithdrawal: timestampProof has incorrect length"
);
require(
withdrawalProof.historicalSummaryBlockRootProof.length ==
32 *
(BEACON_STATE_FIELD_TREE_HEIGHT +
(HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) +
1 +
(BLOCK_ROOTS_TREE_HEIGHT)),
"BeaconChainProofs.verifyWithdrawal: historicalSummaryBlockRootProof has incorrect length"
);
/**
* Note: Here, the "1" in "1 + (BLOCK_ROOTS_TREE_HEIGHT)" signifies that extra step of choosing the "block_root_summary" within the individual
* "historical_summary". Everywhere else it signifies merkelize_with_mixin, where the length of an array is hashed with the root of the array,
* but not here.
*/
uint256 historicalBlockHeaderIndex = (HISTORICAL_SUMMARIES_INDEX <<
((HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) + 1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
(uint256(withdrawalProof.historicalSummaryIndex) << (1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
(BLOCK_SUMMARY_ROOT_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)) |
uint256(withdrawalProof.blockRootIndex);
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.historicalSummaryBlockRootProof,
root: beaconStateRoot,
leaf: withdrawalProof.blockRoot,
index: historicalBlockHeaderIndex
}),
"BeaconChainProofs.verifyWithdrawal: Invalid historicalsummary merkle proof"
);
//Next we verify the slot against the blockRoot
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.slotProof,
root: withdrawalProof.blockRoot,
leaf: withdrawalProof.slotRoot,
index: SLOT_INDEX
}),
"BeaconChainProofs.verifyWithdrawal: Invalid slot merkle proof"
);
{
// Next we verify the executionPayloadRoot against the blockRoot
uint256 executionPayloadIndex = (BODY_ROOT_INDEX << (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)) |
EXECUTION_PAYLOAD_INDEX;
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.executionPayloadProof,
root: withdrawalProof.blockRoot,
leaf: withdrawalProof.executionPayloadRoot,
index: executionPayloadIndex
}),
"BeaconChainProofs.verifyWithdrawal: Invalid executionPayload merkle proof"
);
}
// Next we verify the timestampRoot against the executionPayload root
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.timestampProof,
root: withdrawalProof.executionPayloadRoot,
leaf: withdrawalProof.timestampRoot,
index: TIMESTAMP_INDEX
}),
"BeaconChainProofs.verifyWithdrawal: Invalid timestamp merkle proof"
);
{
/**
* Next we verify the withdrawal fields against the executionPayloadRoot:
* First we compute the withdrawal_index, then we merkleize the
* withdrawalFields container to calculate the withdrawalRoot.
*
* Note: Merkleization of the withdrawals root tree uses MerkleizeWithMixin, i.e., the length of the array is hashed with the root of
* the array. Thus we shift the WITHDRAWALS_INDEX over by WITHDRAWALS_TREE_HEIGHT + 1 and not just WITHDRAWALS_TREE_HEIGHT.
*/
uint256 withdrawalIndex = (WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1)) |
uint256(withdrawalProof.withdrawalIndex);
bytes32 withdrawalRoot = Merkle.merkleizeSha256(withdrawalFields);
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.withdrawalProof,
root: withdrawalProof.executionPayloadRoot,
leaf: withdrawalRoot,
index: withdrawalIndex
}),
"BeaconChainProofs.verifyWithdrawal: Invalid withdrawal merkle proof"
);
}
}
/**
* @notice This function replicates the ssz hashing of a validator's pubkey, outlined below:
* hh := ssz.NewHasher()
* hh.PutBytes(validatorPubkey[:])
* validatorPubkeyHash := hh.Hash()
* hh.Reset()
*/
function hashValidatorBLSPubkey(bytes memory validatorPubkey) internal pure returns (bytes32 pubkeyHash) {
require(validatorPubkey.length == 48, "Input should be 48 bytes in length");
return sha256(abi.encodePacked(validatorPubkey, bytes16(0)));
}
/**
* @dev Retrieve the withdrawal timestamp
*/
function getWithdrawalTimestamp(WithdrawalProof memory withdrawalProof) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(withdrawalProof.timestampRoot);
}
/**
* @dev Converts the withdrawal's slot to an epoch
*/
function getWithdrawalEpoch(WithdrawalProof memory withdrawalProof) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(withdrawalProof.slotRoot) / SLOTS_PER_EPOCH;
}
/**
* Indices for validator fields (refer to consensus specs):
* 0: pubkey
* 1: withdrawal credentials
* 2: effective balance
* 3: slashed?
* 4: activation elligibility epoch
* 5: activation epoch
* 6: exit epoch
* 7: withdrawable epoch
*/
/**
* @dev Retrieves a validator's pubkey hash
*/
function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) {
return
validatorFields[VALIDATOR_PUBKEY_INDEX];
}
function getWithdrawalCredentials(bytes32[] memory validatorFields) internal pure returns (bytes32) {
return
validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX];
}
/**
* @dev Retrieves a validator's effective balance (in gwei)
*/
function getEffectiveBalanceGwei(bytes32[] memory validatorFields) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]);
}
/**
* @dev Retrieves a validator's withdrawable epoch
*/
function getWithdrawableEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
}
/**
* Indices for withdrawal fields (refer to consensus specs):
* 0: withdrawal index
* 1: validator index
* 2: execution address
* 3: withdrawal amount
*/
/**
* @dev Retrieves a withdrawal's validator index
*/
function getValidatorIndex(bytes32[] memory withdrawalFields) internal pure returns (uint40) {
return
uint40(Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_INDEX_INDEX]));
}
/**
* @dev Retrieves a withdrawal's withdrawal amount (in gwei)
*/
function getWithdrawalAmountGwei(bytes32[] memory withdrawalFields) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);
}
}
Merkle.sol 172 lines
// SPDX-License-Identifier: MIT
// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @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 Merkle {
/**
* @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. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* Note this is for a Merkle tree using the keccak/sha3 hash function
*/
function verifyInclusionKeccak(
bytes memory proof,
bytes32 root,
bytes32 leaf,
uint256 index
) internal pure returns (bool) {
return processInclusionProofKeccak(proof, leaf, index) == 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. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* _Available since v4.4._
*
* Note this is for a Merkle tree using the keccak/sha3 hash function
*/
function processInclusionProofKeccak(
bytes memory proof,
bytes32 leaf,
uint256 index
) internal pure returns (bytes32) {
require(
proof.length != 0 && proof.length % 32 == 0,
"Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32"
);
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
if (index % 2 == 0) {
// if ith bit of index is 0, then computedHash is a left sibling
assembly {
mstore(0x00, computedHash)
mstore(0x20, mload(add(proof, i)))
computedHash := keccak256(0x00, 0x40)
index := div(index, 2)
}
} else {
// if ith bit of index is 1, then computedHash is a right sibling
assembly {
mstore(0x00, mload(add(proof, i)))
mstore(0x20, computedHash)
computedHash := keccak256(0x00, 0x40)
index := div(index, 2)
}
}
}
return computedHash;
}
/**
* @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. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* Note this is for a Merkle tree using the sha256 hash function
*/
function verifyInclusionSha256(
bytes memory proof,
bytes32 root,
bytes32 leaf,
uint256 index
) internal view returns (bool) {
return processInclusionProofSha256(proof, leaf, index) == 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. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* _Available since v4.4._
*
* Note this is for a Merkle tree using the sha256 hash function
*/
function processInclusionProofSha256(
bytes memory proof,
bytes32 leaf,
uint256 index
) internal view returns (bytes32) {
require(
proof.length != 0 && proof.length % 32 == 0,
"Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32"
);
bytes32[1] memory computedHash = [leaf];
for (uint256 i = 32; i <= proof.length; i += 32) {
if (index % 2 == 0) {
// if ith bit of index is 0, then computedHash is a left sibling
assembly {
mstore(0x00, mload(computedHash))
mstore(0x20, mload(add(proof, i)))
if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) {
revert(0, 0)
}
index := div(index, 2)
}
} else {
// if ith bit of index is 1, then computedHash is a right sibling
assembly {
mstore(0x00, mload(add(proof, i)))
mstore(0x20, mload(computedHash))
if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) {
revert(0, 0)
}
index := div(index, 2)
}
}
}
return computedHash[0];
}
/**
@notice this function returns the merkle root of a tree created from a set of leaves using sha256 as its hash function
@param leaves the leaves of the merkle tree
@return The computed Merkle root of the tree.
@dev A pre-condition to this function is that leaves.length is a power of two. If not, the function will merkleize the inputs incorrectly.
*/
function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) {
//there are half as many nodes in the layer above the leaves
uint256 numNodesInLayer = leaves.length / 2;
//create a layer to store the internal nodes
bytes32[] memory layer = new bytes32[](numNodesInLayer);
//fill the layer with the pairwise hashes of the leaves
for (uint256 i = 0; i < numNodesInLayer; i++) {
layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
}
//the next layer above has half as many nodes
numNodesInLayer /= 2;
//while we haven't computed the root
while (numNodesInLayer != 0) {
//overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
for (uint256 i = 0; i < numNodesInLayer; i++) {
layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
}
//the next layer above has half as many nodes
numNodesInLayer /= 2;
}
//the first node in the layer is the root
return layer[0];
}
}
Endian.sol 25 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library Endian {
/**
* @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64
* @param lenum little endian-formatted uint64 input, provided as 'bytes32' type
* @return n The big endian-formatted uint64
* @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits)
* through a right-shift/shr operation.
*/
function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) {
// the number needs to be stored in little-endian encoding (ie in bytes 0-8)
n = uint64(uint256(lenum >> 192));
return
(n >> 56) |
((0x00FF000000000000 & n) >> 40) |
((0x0000FF0000000000 & n) >> 24) |
((0x000000FF00000000 & n) >> 8) |
((0x00000000FF000000 & n) << 8) |
((0x0000000000FF0000 & n) << 24) |
((0x000000000000FF00 & n) << 40) |
((0x00000000000000FF & n) << 56);
}
}
Read Contract
OPERATOR_CHURN_APPROVAL_TYPEHASH 0xca0de882 → bytes32
PUBKEY_REGISTRATION_TYPEHASH 0x9feab859 → bytes32
blsApkRegistry 0x5df45946 → address
calculateOperatorChurnApprovalDigestHash 0xaa3f680c → bytes32
churnApprover 0x054310e6 → address
ejector 0x28f61b31 → address
getCurrentQuorumBitmap 0x871ef049 → uint192
getOperator 0x5865c60c → tuple
getOperatorFromId 0x296bb064 → address
getOperatorId 0x13542a4e → bytes32
getOperatorSetParams 0xe65797ad → tuple
getOperatorStatus 0xfd39105a → uint8
getQuorumBitmapAtBlockNumberByIndex 0x04ec6351 → uint192
getQuorumBitmapHistoryLength 0x03fd3492 → uint256
getQuorumBitmapIndicesAtBlockNumber 0xc391425e → uint32[]
getQuorumBitmapUpdateByIndex 0x1eb812da → tuple
indexRegistry 0x9e9923c2 → address
isChurnApproverSaltUsed 0x1478851f → bool
numRegistries 0xd72d8dd6 → uint256
owner 0x8da5cb5b → address
paused 0x5ac86ab7 → bool
paused 0x5c975abb → uint256
pauserRegistry 0x886f1195 → address
pubkeyRegistrationMessageHash 0x3c2a7f4c → tuple
quorumCount 0x9aa1653d → uint8
quorumUpdateBlockNumber 0x249a0c42 → uint256
registries 0x6347c900 → address
serviceManager 0x3998fdd3 → address
stakeRegistry 0x68304835 → address
Write Contract 18 functions
These functions modify contract state and require a wallet transaction to execute.
createQuorum 0x7f2ec40e
tuple operatorSetParams
uint96 minimumStake
tuple[] strategyParams
deregisterOperator 0xca4f2d97
bytes quorumNumbers
ejectOperator 0x6e3b17db
address operator
bytes quorumNumbers
initialize 0x404d5de8
address _initialOwner
address _churnApprover
address _ejector
address _pauserRegistry
uint256 _initialPausedStatus
tuple[] _operatorSetParams
uint96[] _minimumStakes
tuple[][] _strategyParams
pause 0x136439dd
uint256 newPausedStatus
pauseAll 0x595c6a67
No parameters
registerOperator 0x76e3c1c6
bytes quorumNumbers
string socket
tuple params
tuple operatorSignature
registerOperatorWithChurn 0x399063f1
bytes quorumNumbers
string socket
tuple params
tuple[] operatorKickParams
tuple churnApproverSignature
tuple operatorSignature
renounceOwnership 0x715018a6
No parameters
setChurnApprover 0x29d1e0c3
address _churnApprover
setEjector 0x2cdd1e86
address _ejector
setOperatorSetParams 0x1069589b
uint8 quorumNumber
tuple operatorSetParams
setPauserRegistry 0x10d67a2f
address newPauserRegistry
transferOwnership 0xf2fde38b
address newOwner
unpause 0xfabc1cbc
uint256 newPausedStatus
updateOperators 0x00cf2ab5
address[] operators
updateOperatorsForQuorum 0x5140a548
address[][] operatorsPerQuorum
bytes quorumNumbers
updateSocket 0x0cf4b767
string socket
Recent Transactions
No transactions found for this address