Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x0C5BC4C8406Fe03214D18bbf2962Ae2fa378c6f7
Balance 0 ETH
Nonce 1
Code Size 24481 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

24481 bytes
0x608060405234801561000f575f80fd5b5060043610610490575f3560e01c806372920c9011610263578063b460af941161014b578063d547741f116100ca578063e4c4be581161008f578063e4c4be5814610abb578063ec94c45214610ac3578063ef8b30f714610a2e578063f24d655014610ad6578063f429196814610ae9578063fa3468b814610afc575f80fd5b8063d547741f14610a67578063d6276b2614610a7a578063d905777e14610a8d578063db7a3ea314610aa0578063dd62ed3e14610aa8575f80fd5b8063c2e9dcd611610110578063c2e9dcd6146109f4578063c63d75b614610a1b578063c6e6f59214610a2e578063ca15c87314610a41578063ce96cb7714610a54575f80fd5b8063b460af9414610978578063ba0876521461098b578063baaf36b51461099e578063c012cf77146109b2578063c19a2aa7146109c5575f80fd5b806395d89b41116101e2578063a4d66daf116101a7578063a4d66daf14610903578063a9059cbb1461092d578063abc5f1bc14610940578063aeb0f16414610955578063b3d7f6b91461095d578063b42b544f14610970575f80fd5b806395d89b41146108ae5780639bd0911b146108b65780639bfe9e23146108d6578063a217fddf146108e9578063a2861466146108f0575f80fd5b80638ebd0098116102285780638ebd00981461084f5780639010d07c1461086257806391d1485414610875578063922bafd01461088857806394bf804d1461089b575f80fd5b806372920c9014610811578063794b15b7146108195780637a41ac321461082c5780637d7c2a1c1461083f578063870ba74614610847575f80fd5b8063313ce567116103865780634b6a58fe1161030557806356bb54a7116102ca57806356bb54a7146107a957806363d8882a146107b157806365e82ce3146107b95780636e22558d146107c15780636e553f65146107eb57806370a08231146107fe575f80fd5b80634b6a58fe146107495780634cdad506146104f15780634dc3e37b1461075c5780634f62421f1461076f5780635680e14514610782575f80fd5b80633a2bbe491161034b5780633a2bbe49146106af5780633e3cc239146106d6578063402d267d146106f65780634105a7dd1461070957806348d3b7751461071c575f80fd5b8063313ce5671461065f57806332ca09971461067957806336568abe1461068157806336f606191461069457806338d52e0f146106a7575f80fd5b806318160ddd1161041257806327daaaac116103d757806327daaaac146105ec57806327ea6f2b146105ff5780632b181e38146106125780632e2d2984146106395780632f2ff15d1461064c575f80fd5b806318160ddd146105845780631dcae164146105ab57806323b872dd146105b3578063248a9ca3146105c657806327cfa7ef146105d9575f80fd5b8063095ea7b311610458578063095ea7b3146105045780630a28a477146105175780630a8e214c1461052a5780630dce84561461053257806313462e7214610559575f80fd5b806301e1d1141461049457806301ffc9a7146104af57806302191980146104d257806306fdde03146104dc57806307a2d13a146104f1575b5f80fd5b61049c610b10565b6040519081526020015b60405180910390f35b6104c26104bd36600461512e565b610df4565b60405190151581526020016104a6565b6104da610e1e565b005b6104e4610e80565b6040516104a69190615183565b61049c6104ff366004615195565b610f40565b6104c26105123660046151c0565b610f4b565b61049c610525366004615195565b610f62565b61049c610f6e565b61049c7f52a2e0e4c67d823979c1b45c7474d67476e3760d4e4797c5ad3a000c3e72a9cf81565b61056c6105673660046151f8565b610f87565b6040516001600160a01b0390911681526020016104a6565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025461049c565b61056c611054565b6104c26105c1366004615211565b61106f565b61049c6105d4366004615195565b611094565b6104da6105e736600461524f565b6110b4565b6104da6105fa36600461524f565b611146565b6104da61060d366004615195565b61118c565b61049c7f5e9c3ed0ef5487d8cda3fc51646c35847fdc0325200c5a21a159b36d8cba882e81565b61049c61064736600461526a565b6111bf565b6104da61065a3660046152a9565b61121e565b610667611240565b60405160ff90911681526020016104a6565b61056c61126f565b6104da61068f3660046152a9565b61128a565b6104da6106a236600461524f565b6112c2565b61056c611433565b61049c7fae944fcb809c01a95e89b6931e61939dd0bbb790c044c20197d070090098c3a281565b6106e96106e4366004615195565b61144e565b6040516104a6919061530b565b61049c61070436600461524f565b6115ad565b6104da61071736600461537b565b611695565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a1006002015460ff166104c2565b6104da610757366004615396565b6116c8565b61049c61076a366004615195565b611863565b6104da61077d36600461524f565b611879565b61049c7f2c09deb013d6a86a9063c95b5e2b10937d1128a3937347eb7e00408ed9fb1a9c81565b6104da61190e565b6104da61196c565b61056c6119a2565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a1005460ff166104c2565b61049c6107f93660046152a9565b6119bd565b61049c61080c36600461524f565b6119e8565b61056c611a0e565b6104c261082736600461524f565b611a29565b6104da61083a3660046153c9565b611a68565b6104da611dab565b61056c61204e565b61049c61085d36600461524f565b612066565b61056c610870366004615407565b612090565b6104c26108833660046152a9565b6120b5565b6104da610896366004615427565b6120eb565b61049c6108a93660046152a9565b612362565b6104e4612375565b6108c96108c4366004615195565b6123b3565b6040516104a6919061549b565b6104da6108e43660046154d7565b612463565b61049c5f81565b6104da6108fe36600461550e565b612773565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a1006001015461049c565b6104c261093b3660046151c0565b6127a7565b6109486127b4565b6040516104a6919061553a565b61056c6127c9565b61049c61096b366004615195565b6127e4565b61049c6127f0565b61049c61098636600461526a565b612802565b61049c61099936600461526a565b61282e565b61049c5f80516020615e8c83398151915281565b6104da6109c036600461524f565b612842565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a10054610100900460ff166104c2565b61049c7f4c30aa7f742e0a14ed7755cb202fc37abe01363d805182186c7cfe8f8a15d64c81565b61049c610a2936600461524f565b612888565b61049c610a3c366004615195565b6128ab565b61049c610a4f366004615195565b6128b6565b61049c610a6236600461524f565b6128da565b6104da610a753660046152a9565b61291b565b6104c2610a88366004615195565b612937565b61049c610a9b36600461524f565b61294d565b61056c61298e565b61049c610ab636600461557d565b6129a9565b6104da6129f2565b6104da610ad136600461524f565b612a25565b6104da610ae436600461524f565b612a58565b6104da610af736600461524f565b612a9e565b61049c5f80516020615eec83398151915281565b5f30610b1a611433565b6040516370a0823160e01b81526001600160a01b03838116600483015291909116906370a0823190602401602060405180830381865afa158015610b60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b8491906155a9565b91505f610b8f6127c9565b90506001600160a01b03811615610c15576040516370a0823160e01b81526001600160a01b0383811660048301528216906370a0823190602401602060405180830381865afa158015610be4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c0891906155a9565b610c1290846155d4565b92505b5f610c1e6127f0565b9050610c436040805160608101909152805f81525f6020820181905260409091015290565b5f5b82811015610dec57610c56816123b3565b9150610c64825f0151610f87565b602083015160405163779b5def60e11b81526001600160a01b03918216600482015291169063ef36bbde90602401602060405180830381865afa158015610cad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cd191906155a9565b610cdb90876155d4565b60408301519096506001600160a01b031615610de45760408083015190516363c6b4eb60e01b81526001600160a01b038781166004830152909116906363c6b4eb90602401602060405180830381865afa158015610d3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d5f91906155a9565b604080840151905163e7beaf9d60e01b81526001600160a01b0388811660048301529091169063e7beaf9d90602401602060405180830381865afa158015610da9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd91906155a9565b610dd791906155d4565b610de190876155d4565b95505b600101610c45565b505050505090565b5f6001600160e01b03198216635a05180f60e01b1480610e185750610e1882612b32565b92915050565b7fb0cb335afe1cba10c746b42a1e019651022572bbc76c3f63338df5dd3abb9894610e4881612b66565b610e526001612b70565b610e7c7fb0cb335afe1cba10c746b42a1e019651022572bbc76c3f63338df5dd3abb989433612bf0565b5050565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0380546060915f80516020615ecc83398151915291610ebe906155e7565b80601f0160208091040260200160405190810160405280929190818152602001828054610eea906155e7565b8015610f355780601f10610f0c57610100808354040283529160200191610f35565b820191905f5260205f20905b815481529060010190602001808311610f1857829003601f168201915b505050505091505090565b5f610e18825f612c32565b5f33610f58818585612c89565b5060019392505050565b5f610e18826001612c96565b5f610f82610f7a612ce4565b600601612d08565b905090565b5f80826002811115610f9b57610f9b6152d7565b03610faf57610fa86119a2565b9050610ff4565b6001826002811115610fc357610fc36152d7565b03610fd057610fa8611a0e565b6002826002811115610fe457610fe46152d7565b03610ff457610ff161126f565b90505b6001600160a01b03811661104f5760405162461bcd60e51b815260206004820181905260248201527f4d756c74695661756c743a20756e737570706f727465642070726f746f636f6c60448201526064015b60405180910390fd5b919050565b5f61105d612ce4565b600101546001600160a01b0316919050565b5f3361107c858285612d11565b611087858585612d5b565b60019150505b9392505050565b5f9081525f80516020615f0c833981519152602052604090206001015490565b5f80516020615e8c8339815191526110cb81612b66565b6001600160a01b03821661113d5760405162461bcd60e51b815260206004820152603360248201527f4d756c74695661756c743a206465706f7369742073747261746567792063616e6044820152726e6f74206265207a65726f206164647265737360681b6064820152608401611046565b610e7c82612db8565b5f80516020615eec83398151915261115d81612b66565b6001600160a01b0382166111835760405162461bcd60e51b81526004016110469061561f565b610e7c82612e0a565b7f6e5811d60b7d57973a97208b6158fed3b8e064ca747403e6a8c81f56a8f9e75f6111b681612b66565b610e7c82612e5f565b5f6111ca84846119bd565b604080518681526001600160a01b03868116602083015285168183015290519192507f0f33e518e0001973b4f02d7cef360e8c9290e9ebd0b48695c935af9bac850213919081900360600190a19392505050565b61122782611094565b61123081612b66565b61123a8383612ec6565b50505050565b5f805f80516020615f2c83398151915290505f81546112699190600160a01b900460ff16615669565b91505090565b5f611278612ce4565b600b01546001600160a01b0316919050565b6001600160a01b03811633146112b35760405163334bd91960e11b815260040160405180910390fd5b6112bd8282612bf0565b505050565b7f52a2e0e4c67d823979c1b45c7474d67476e3760d4e4797c5ad3a000c3e72a9cf6112ec81612b66565b5f6112f56127c9565b6001600160a01b031614801561131357506001600160a01b03821615155b6113935760405162461bcd60e51b8152602060048201526044602482018190527f4d756c74695661756c743a2064656661756c7420636f6c6c61746572616c2061908201527f6c726561647920736574206f722063616e6e6f74206265207a65726f206164646064820152637265737360e01b608482015260a401611046565b61139b611433565b6001600160a01b0316826001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113e0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114049190615682565b6001600160a01b03161461142a5760405162461bcd60e51b81526004016110469061569d565b610e7c82612eff565b5f80516020615f2c833981519152546001600160a01b031690565b6114866040805160c0810182525f80825260208201819052918101829052606081018290529060808201908152602001606081525090565b61148e612ce4565b5f8381526005919091016020908152604091829020825160c08101845281546001600160a01b0390811682526001830154811693820193909352600280830154909316938101939093526003810154606084015260048101549091608084019160ff1690811115611501576115016152d7565b6002811115611512576115126152d7565b8152602001600582018054611526906155e7565b80601f0160208091040260200160405190810160405280929190818152602001828054611552906155e7565b801561159d5780601f106115745761010080835404028352916020019161159d565b820191905f5260205f20905b81548152906001019060200180831161158057829003601f168201915b5050505050815250509050919050565b5f6115d97f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a1005460ff1690565b156115e557505f919050565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a1006002015460ff16801561161f575061161d82611a29565b155b1561162b57505f919050565b5f61165760017f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100015490565b90505f19810361166a57505f1992915050565b5f611673610b10565b905080821015611683575f61168d565b61168d8183615706565b949350505050565b7f1b628514858a9a09af73c606e9892743b44cfbdd0c49a7279286ed58eaafcf6d6116bf81612b66565b610e7c82612f54565b7fae944fcb809c01a95e89b6931e61939dd0bbb790c044c20197d070090098c3a26116f281612b66565b5f6116fc83610f87565b9050611706611433565b6040516371f9621160e01b81526001600160a01b038681166004830152918216918316906371f9621190602401602060405180830381865afa15801561174e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117729190615682565b6001600160a01b0316146117ee5760405162461bcd60e51b815260206004820152603960248201527f4d756c74695661756c743a207375627661756c7420617373657420646f65732060448201527f6e6f74206d6174636820746865207661756c74206173736574000000000000006064820152608401611046565b60405163c28ef59160e01b81526001600160a01b03808616600483015261123a91869184169063c28ef591906024016020604051808303815f875af1158015611839573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061185d9190615682565b85612fb1565b5f610e1882611870612ce4565b60060190613169565b5f80516020615e8c83398151915261189081612b66565b6001600160a01b0382166119055760405162461bcd60e51b815260206004820152603660248201527f4d756c74695661756c743a207769746864726177616c2073747261746567792060448201527563616e6e6f74206265207a65726f206164647265737360501b6064820152608401611046565b610e7c82613174565b7ffd367f64dba4612ce1d6ed7e93d2495a59d305f21fba3e15126e1ad62aa9ea1561193881612b66565b61194260016131c9565b610e7c7ffd367f64dba4612ce1d6ed7e93d2495a59d305f21fba3e15126e1ad62aa9ea1533612bf0565b7f49604999f0c13f9a566b3eb2608f60c199b1a3a43d8ff8032fe66a465c945d1d61199681612b66565b61199f5f612b70565b50565b5f6119ab612ce4565b600901546001600160a01b0316919050565b5f6119c6613224565b6119d0838361325b565b9050610e1860015f80516020615f4c83398151915255565b6001600160a01b03165f9081525f80516020615ecc833981519152602052604090205490565b5f611a17612ce4565b600a01546001600160a01b0316919050565b6001600160a01b03165f90815260037f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a10001602052604090205460ff1690565b7f4c30aa7f742e0a14ed7755cb202fc37abe01363d805182186c7cfe8f8a15d64c611a9281612b66565b5f611aa3606084016040850161524f565b6001600160a01b031614611d9957611ab9611433565b6001600160a01b0316611ad2606084016040850161524f565b6001600160a01b031614158015611b115750611aec6127c9565b6001600160a01b0316611b05606084016040850161524f565b6001600160a01b031614155b611b9a5760405162461bcd60e51b815260206004820152604e60248201527f4d756c74695661756c743a2072657761726420746f6b656e2063616e6e6f742060448201527f6265207468652073616d6520617320746865206173736574206f72206465666160648201526d1d5b1d0818dbdb1b185d195c985b60921b608482015260a401611046565b620f424082606001351115611bfd5760405162461bcd60e51b8152602060048201526024808201527f4d756c74695661756c743a2063757261746f72206665652065786365656473206044820152633130302560e01b6064820152608401611046565b5f611c0b602084018461524f565b6001600160a01b031603611c7e5760405162461bcd60e51b815260206004820152603460248201527f4d756c74695661756c743a20646973747269627574696f6e206661726d20616460448201527364726573732063616e6e6f74206265207a65726f60601b6064820152608401611046565b606082013515611d21575f611c99604084016020850161524f565b6001600160a01b031603611d215760405162461bcd60e51b815260206004820152604360248201527f4d756c74695661756c743a2063757261746f722074726561737572792061646460448201527f726573732063616e6e6f74206265207a65726f207768656e20666565206973206064820152621cd95d60ea1b608482015260a401611046565b611d3461056760a08401608085016151f8565b6001600160a01b0316638e6adb03611d4f60a0850185615719565b6040518363ffffffff1660e01b8152600401611d6c92919061578a565b5f6040518083038186803b158015611d82575f80fd5b505afa158015611d94573d5f803e3d5ffd5b505050505b6112bd83611da684615874565b6132ba565b7f2c09deb013d6a86a9063c95b5e2b10937d1128a3937347eb7e00408ed9fb1a9c611dd581612b66565b305f611ddf61298e565b6040516318deda0d60e21b81526001600160a01b038481166004830152919091169063637b6834906024015f60405180830381865afa158015611e24573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611e4b91908101906159f6565b90505f5b8151811015611ec457611ebc828281518110611e6d57611e6d615a3a565b60200260200101515f0151838381518110611e8a57611e8a615a3a565b6020026020010151606001515f858581518110611ea957611ea9615a3a565b602002602001015160400151878861346d565b600101611e4f565b505f611ece6127c9565b90506001600160a01b03811615611fb0576040516370a0823160e01b81526001600160a01b0384811660048301525f91908316906370a0823190602401602060405180830381865afa158015611f26573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f4a91906155a9565b90508015611fae5760405163f3fef3a360e01b81526001600160a01b0383169063f3fef3a390611f809087908590600401615a4e565b5f604051808303815f87803b158015611f97575f80fd5b505af1158015611fa9573d5f803e3d5ffd5b505050505b505b5f5b825181101561200657611ffe838281518110611fd057611fd0615a3a565b60200260200101515f0151848381518110611fed57611fed615a3a565b6020026020010151602001516135f5565b600101611fb2565b5061200f613661565b7f7d6777c735b10f616d6f9d64f06e49370db2f04a19bfc849efe6ce8440cefd038242604051612040929190615a67565b60405180910390a150505050565b5f612057612ce4565b546001600160a01b0316919050565b5f61206f612ce4565b6001600160a01b039092165f90815260049290920160205250604090205490565b5f8281525f80516020615eac83398151915260208190526040822061168d9084613169565b5f9182525f80516020615f0c833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6120f3613224565b6120fc83612937565b6121485760405162461bcd60e51b815260206004820152601a60248201527f4d756c74695661756c743a206661726d206e6f7420666f756e640000000000006044820152606401611046565b5f6121528461144e565b60408181015190516370a0823160e01b815230600482018190529293509091905f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156121a3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c791906155a9565b90506122216121d98560800151610f87565b60a08601516040516121f39187918b918b91602401615ad3565b60408051601f198184030181529190526020810180516001600160e01b031662f73b2160e11b1790526138a6565b506040516370a0823160e01b81526001600160a01b0383811660048301528291908516906370a0823190602401602060405180830381865afa158015612269573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061228d91906155a9565b6122979190615706565b9050805f036122a9575050505061234c565b60608401515f906122bf908390620f4240613918565b905080156122e15760208501516122e1906001600160a01b03861690836139d7565b6122eb8183615706565b9150811561230a57845161230a906001600160a01b03861690846139d7565b8082897f12d8e9280ac85b1633cd74c52f9458f73459aecd6eb0f2d99e2ed27c3fa27e994260405161233e91815260200190565b60405180910390a450505050505b6112bd60015f80516020615f4c83398151915255565b5f61236b613224565b6119d08383613a2f565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060915f80516020615ecc83398151915291610ebe906155e7565b604080516060810182525f80825260208201819052918101919091526123d7612ce4565b60030182815481106123eb576123eb615a3a565b905f5260205f2090600202016040518060600160405290815f82015f9054906101000a900460ff166002811115612424576124246152d7565b6002811115612435576124356152d7565b815281546001600160a01b036101009091048116602083015260019092015490911660409091015292915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460029190600160401b900460ff16806124ac575080546001600160401b03808416911610155b156124ca5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b03831617600160401b1781556125c76124fb602085018561524f565b6020850135612510606087016040880161537b565b612520608088016060890161537b565b61253060a0890160808a0161537b565b61254060c08a0160a08b0161524f565b61254d60c08b018b615719565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061258e9250505060e08c018c615719565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613a7b92505050565b61264d6125dc6101208501610100860161524f565b6125ee6101408601610120870161524f565b6126006101608701610140880161524f565b6126126101808801610160890161524f565b6126246101a089016101808a0161524f565b6126366101c08a016101a08b0161524f565b6126486101e08b016101c08c0161524f565b613aa3565b5f6126606101808501610160860161524f565b6001600160a01b03161480612705575061268060c0840160a0850161524f565b6001600160a01b031661269b6101808501610160860161524f565b6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156126d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126fa9190615682565b6001600160a01b0316145b6127215760405162461bcd60e51b81526004016110469061569d565b805468ff0000000000000000191681556040516001600160401b03831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020015b60405180910390a1505050565b7f1867ae69910bc7238ccbbb445aa11a0dbdd472b851b7fac0c991101aca0a360d61279d81612b66565b6112bd8383613af3565b5f33610f58818585612d5b565b6060610f826127c1612ce4565b600601613b82565b5f6127d2612ce4565b600801546001600160a01b0316919050565b5f610e18826001612c32565b5f6127f9612ce4565b60030154919050565b5f61280b613224565b612816848484613b8e565b905061108d60015f80516020615f4c83398151915255565b5f612837613224565b612816848484613bdb565b5f80516020615eec83398151915261285981612b66565b6001600160a01b03821661287f5760405162461bcd60e51b81526004016110469061561f565b610e7c82613c28565b5f80612893836115ad565b90505f1981036128a657505f1992915050565b61108d815b5f610e18825f612c96565b5f8181525f80516020615eac83398151915260208190526040822061108d90612d08565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100545f90610100900460ff161561291257505f919050565b610e1882613c7d565b61292482611094565b61292d81612b66565b61123a8383612bf0565b5f610e1882612944612ce4565b60060190613c90565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100545f90610100900460ff161561298557505f919050565b610e1882613ca7565b5f612997612ce4565b600201546001600160a01b0316919050565b6001600160a01b039182165f9081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b7f8233fa2806b76ee3f3ba048be2bf735b5c73b63051394e82774bfd7be0db6c02612a1c81612b66565b61199f5f6131c9565b7f5e9c3ed0ef5487d8cda3fc51646c35847fdc0325200c5a21a159b36d8cba882e612a4f81612b66565b610e7c82613cb1565b5f80516020615eec833981519152612a6f81612b66565b6001600160a01b038216612a955760405162461bcd60e51b81526004016110469061561f565b610e7c82613f96565b5f80516020615e8c833981519152612ab581612b66565b6001600160a01b038216612b295760405162461bcd60e51b815260206004820152603560248201527f4d756c74695661756c743a20726562616c616e63652073747261746567792063604482015274616e6e6f74206265207a65726f206164647265737360581b6064820152608401611046565b610e7c82613feb565b5f6001600160e01b03198216637965db0b60e01b1480610e1857506301ffc9a760e01b6001600160e01b0319831614610e18565b61199f8133614040565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100805460ff19168215151781557f917e249a9d1439adce72cf39ffef9f3ae18f5b7b37a942393522093db79826c48242335b60408051931515845260208401929092526001600160a01b0316908201526060015b60405180910390a15050565b5f5f80516020615eac83398151915281612c0a858561406b565b9050801561168d575f858152602083905260409020612c2990856140ed565b50949350505050565b5f61108d612c3e610b10565b612c499060016155d4565b612c545f600a615bf4565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0254612c8091906155d4565b85919085614101565b6112bd838383600161414e565b5f61108d612ca582600a615bf4565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0254612cd191906155d4565b612cd9610b10565b612c809060016155d4565b7fc3a707aa51938bbe50b8f0a96369d42dda27c934d557a5e2683794ff43ede80090565b5f610e18825490565b5f612d1c84846129a9565b90505f19811461123a5781811015612d4d57828183604051637dc7a0d960e11b815260040161104693929190615c02565b61123a84848484035f61414e565b6001600160a01b038316612d8457604051634b637e8f60e11b81525f6004820152602401611046565b6001600160a01b038216612dad5760405163ec442f0560e01b81525f6004820152602401611046565b6112bd838383614232565b80612dc1612ce4565b80546001600160a01b0319166001600160a01b03928316179055604051908216907f57a5bf62a4353ce26708dda1c4240c61d1f2a3ce4658bce8b8323728a0c283b3905f90a250565b80612e13612ce4565b600a0180546001600160a01b0319166001600160a01b03928316179055604051908216907fb1f25f229f7d29bb2869f260cec244dd18abe52ff38efebde8f41a2df26f3f95905f90a250565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100600181018290556040805183815242602082015233918101919091527f085b30415f63cd5f875aa6df7b116b64821bdea341dabcb10c94f1cc997138d990606001612be4565b5f5f80516020615eac83398151915281612ee08585614358565b9050801561168d575f858152602083905260409020612c2990856143f0565b80612f08612ce4565b60080180546001600160a01b0319166001600160a01b03928316179055604051908216907fe61ffa215563bbcd71660e505a198954816744db96961d4f0e129d70496223aa905f90a250565b5f7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a10060028101805460ff191684151517905590507ffbf9c5de1d63473d377197b4f8bbe7d49c129878194966479bcd64085ad32c55824233612bc2565b5f612fba612ce4565b6001600160a01b0385165f908152600482016020526040902054909150156130375760405162461bcd60e51b815260206004820152602a60248201527f4d756c74695661756c7453746f726167653a207375627661756c7420616c72656044820152696164792065786973747360b01b6064820152608401611046565b806003016040518060600160405280846002811115613058576130586152d7565b81526001600160a01b038088166020808401919091529087166040909201919091528254600181810185555f94855291909320825160029485029091018054939490939192849260ff19169184908111156130b5576130b56152d7565b02179055506020828101518254610100600160a81b0319166101006001600160a01b0392831602178355604093840151600193840180546001600160a01b03191691831691909117905560038501549088165f818152600487019093529390912081905591907ffebd51bd4d415b6b7c651e6d091508df5dccf8d4eec301d265316cb58bb5807c908690869061314b9086615706565b60405161315a93929190615c23565b60405180910390a25050505050565b5f61108d8383614404565b8061317d612ce4565b60010180546001600160a01b0319166001600160a01b03928316179055604051908216907fb58f6e54620af05d02db6852929c91e5ef2a7c8f5344d5665649cca4cac9369c905f90a250565b7f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100805461ff001916610100831515021781557f100e52ba225b1d864d8b4e3725311b44dc3d490024db41251211c87c20da20a7824233612bc2565b5f80516020615f4c83398151915280546001190161325557604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b5f80613266836115ad565b90508084111561328f57828482604051633c8097d960e11b815260040161104693929190615c02565b5f613299856128ab565b905061168d3385878461442a565b60015f80516020615f4c83398151915255565b5f6132c3612ce4565b60408301519091506001600160a01b0316613371576132e56006820184614556565b156112bd575f83815260058083016020526040822080546001600160a01b031990811682556001820180548216905560028201805490911690556003810183905560048101805460ff191690559190613340908301826150e4565b505060405183907fdcde38f1f06860d959fbffac6d32c9676b594e5176f3041a9643b65efd52cc50905f90a2505050565b5f838152600582016020908152604091829020845181546001600160a01b03199081166001600160a01b03928316178355928601516001808401805486169284169290921790915593860151600280840180549095169190921617909255606085015160038201556080850151600482018054879593949293919260ff1990911691908490811115613405576134056152d7565b021790555060a0820151600582019061341e9082615c92565b5061342f9150506006820184614561565b50827f15c73dd4232bbf5a34b3a1bbebf13c0170d2bdf118c4b59829917a955aecda7283604051613460919061530b565b60405180910390a2505050565b5f613477876123b3565b905030841561350057604080830151905163132d974d60e31b81526001600160a01b0383811660048301528581166024830152604482018890529091169063996cba68906064016020604051808303815f875af11580156134da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134fe91906155a9565b505b85156135665781604001516001600160a01b03166347f2876484886040518363ffffffff1660e01b8152600401613538929190615a4e565b5f604051808303815f87803b15801561354f575f80fd5b505af1158015613561573d5f803e3d5ffd5b505050505b86156135eb576135e961357b835f0151610f87565b602084015160408086015190516001600160a01b03928316602482015290821660448201528187166064820152608481018b905290871660a482015260c40160408051601f198184030181529190526020810180516001600160e01b031663413b4bab60e01b1790526138a6565b505b5050505050505050565b805f03613600575050565b5f61360a836123b3565b905061123a61361b825f0151610f87565b826020015184604051602401613632929190615a4e565b60408051601f198184030181529190526020810180516001600160e01b03166311f9fbc960e21b1790526138a6565b5f61366a6127c9565b90506001600160a01b03811661367d5750565b5f816001600160a01b031663a4d66daf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ba573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136de91906155a9565b90505f826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561371d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061374191906155a9565b905081811061374f57505050565b305f613759611433565b90505f6137d76137698587615706565b6040516370a0823160e01b81526001600160a01b0386811660048301528516906370a0823190602401602060405180830381865afa1580156137ad573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137d191906155a9565b9061456c565b9050805f036137e857505050505050565b6137fc6001600160a01b0383168783614581565b6040516311f9fbc960e21b81526001600160a01b038716906347e7ef249061382a9086908590600401615a4e565b6020604051808303815f875af1158015613846573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061386a91906155a9565b506040518181527fc93a98ddade84b56128f8ea5075b56ebe12c1798495023db3b798fff096e8a769060200160405180910390a1505050505050565b60605f80846001600160a01b0316846040516138c29190615d4d565b5f60405180830381855af49150503d805f81146138fa576040519150601f19603f3d011682016040523d82523d5f602084013e6138ff565b606091505b509150915061390f858383614608565b95945050505050565b5f838302815f1985870982811083820303915050805f0361394c5783828161394257613942615d63565b049250505061108d565b80841161396c5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6112bd83846001600160a01b031663a9059cbb85856040516024016139fd929190615a4e565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614664565b5f80613a3a83612888565b905080841115613a635782848260405163284ff66760e01b815260040161104693929190615c02565b5f613a6d856127e4565b905061168d3385838861442a565b613a836146c5565b613a908888888888614710565b613a9a828261473f565b6135eb83614751565b613aab6146c5565b613ab487612db8565b613abd86613174565b613ac685613feb565b613acf84612eff565b613ad883613c28565b613ae182612e0a565b613aea81613f96565b50505050505050565b6001600160a01b0382165f8181527f0f9faf599d288868177261054252fb5009e48eb6456bc1d098cfee92a283a100600381016020908152604092839020805460ff19168615159081179091558351948552908401524291830191909152336060830152907fd010c5e37d148720c8d15b0b786a180ffe7dfc2f80328bdf924354969c5c135290608001612766565b60605f61108d83614762565b5f80613b99836128da565b905080851115613bc257828582604051633fa733bb60e21b815260040161104693929190615c02565b5f613bcc86610f62565b905061390f33868689856147bb565b5f80613be68361294d565b905080851115613c0f57828582604051632e52afbb60e21b815260040161104693929190615c02565b5f613c1986610f40565b905061390f338686848a6147bb565b80613c31612ce4565b60090180546001600160a01b0319166001600160a01b03928316179055604051908216907f0e7c3f2c5c49a4ce154b6342c1538c04dceec744c29fd76035271c53f2b23f11905f90a250565b5f610e18613c8a836119e8565b5f612c32565b5f818152600183016020526040812054151561108d565b5f610e18826119e8565b5f613cba612ce4565b6001600160a01b0383165f908152600482016020526040812054919250819003613d345760405162461bcd60e51b815260206004820152602560248201527f4d756c74695661756c7453746f726167653a207375627661756c74206e6f7420604482015264199bdd5b9960da1b6064820152608401611046565b80613d3e81615d77565b60038401549092505f9150613d5590600190615706565b9050836001600160a01b03167fb8b7f11b26706459148d44bc9626a8b42fb00990e2984ea858d31473028f938c83604051613d9291815260200190565b60405180910390a280821015613f2d575f836003018281548110613db857613db8615a3a565b905f5260205f2090600202016040518060600160405290815f82015f9054906101000a900460ff166002811115613df157613df16152d7565b6002811115613e0257613e026152d7565b815281546001600160a01b0361010090910481166020830152600190920154909116604090910152600385018054919250829185908110613e4557613e45615a3a565b905f5260205f2090600202015f820151815f015f6101000a81548160ff02191690836002811115613e7857613e786152d7565b021790555060208201518154610100600160a81b0319166101006001600160a01b0392831602178255604090920151600191820180546001600160a01b0319169190931617909155613ecb9084906155d4565b602080830180516001600160a01b039081165f908152600489018452604090819020949094559051835186815292830187905216917f4ce16b7eb771d790871156aeacfdfe07f33a3c2fa7bf7b1cfdd350f9e851b395910160405180910390a2505b82600301805480613f4057613f40615d8c565b5f828152602080822060025f199094019384020180546001600160a81b031916815560010180546001600160a01b0319169055919092556001600160a01b03959095168152600490930190935250604081205550565b80613f9f612ce4565b600b0180546001600160a01b0319166001600160a01b03928316179055604051908216907fe99804b588c1291264fc94b636890b2ffae532000bc9787bf8d007d86ba2cbd8905f90a250565b80613ff4612ce4565b60020180546001600160a01b0319166001600160a01b03928316179055604051908216907fc4434beee95edde4a140860318574223d62c98e84a71c9972b45e797d3b53b86905f90a250565b61404a82826120b5565b610e7c57808260405163e2517d3f60e01b8152600401611046929190615a4e565b5f5f80516020615f0c83398151915261408484846120b5565b156140e4575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610e18565b5f915050610e18565b5f61108d836001600160a01b038416614aa0565b5f8061410e868686613918565b905061411983614b7a565b801561413457505f848061412f5761412f615d63565b868809115b1561390f576141446001826155d4565b9695505050505050565b5f80516020615ecc8339815191526001600160a01b0385166141855760405163e602df0560e01b81525f6004820152602401611046565b6001600160a01b0384166141ae57604051634a1406b160e11b81525f6004820152602401611046565b6001600160a01b038086165f9081526001830160209081526040808320938816835292905220839055811561422b57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258560405161422291815260200190565b60405180910390a35b5050505050565b5f80516020615ecc8339815191526001600160a01b03841661426c5781816002015f82825461426191906155d4565b909155506142c99050565b6001600160a01b0384165f90815260208290526040902054828110156142ab5784818460405163391434e360e21b815260040161104693929190615c02565b6001600160a01b0385165f9081526020839052604090209083900390555b6001600160a01b0383166142e7576002810180548390039055614305565b6001600160a01b0383165f9081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161434a91815260200190565b60405180910390a350505050565b5f5f80516020615f0c83398151915261437184846120b5565b6140e4575f848152602082815260408083206001600160a01b03871684529091529020805460ff191660011790556143a63390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610e18565b5f61108d836001600160a01b038416614ba6565b5f825f01828154811061441957614419615a3a565b905f5260205f200154905092915050565b305f61443461204e565b6001600160a01b031663ffe7c26183866040518363ffffffff1660e01b8152600401614461929190615a4e565b5f60405180830381865afa15801561447b573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526144a29190810190615da0565b90506144b086868686614bf2565b5f5b8151811015614545578181815181106144cd576144cd615a3a565b6020026020010151602001515f1461453d576145118282815181106144f4576144f4615a3a565b60200260200101515f0151838381518110611fed57611fed615a3a565b81818151811061452357614523615a3a565b6020026020010151602001518561453a9190615706565b94505b6001016144b2565b5061454e613661565b505050505050565b5f61108d8383614aa0565b5f61108d8383614ba6565b5f81831061457a578161108d565b5090919050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa1580156145ce573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906145f291906155a9565b905061123a848461460385856155d4565b614c6f565b60608261461d5761461882614cff565b61108d565b815115801561463457506001600160a01b0384163b155b1561465d57604051639996b31560e01b81526001600160a01b0385166004820152602401611046565b508061108d565b5f6146786001600160a01b03841683614d28565b905080515f1415801561469c57508080602001905181019061469a9190615e43565b155b156112bd57604051635274afe760e01b81526001600160a01b0384166004820152602401611046565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661470e57604051631afcd79f60e31b815260040160405180910390fd5b565b6147186146c5565b614720614d35565b614728614d45565b6147325f86612ec6565b5061422b84848484614d4d565b6147476146c5565b610e7c8282614d79565b6147596146c5565b61199f81614dc9565b6060815f018054806020026020016040519081016040528092919081815260200182805480156147af57602002820191905f5260205f20905b81548152602001906001019080831161479b575b50505050509050919050565b305f6147c5611054565b6001600160a01b031663fc59ca2b83866040518363ffffffff1660e01b81526004016147f2929190615a4e565b5f60405180830381865afa15801561480c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261483391908101906159f6565b9050846001600160a01b0316876001600160a01b03161461485957614859858885612d11565b6148638584614e39565b5f84905061488e60405180608001604052805f81526020015f81526020015f81526020015f81525090565b5f5b8351811015614909578381815181106148ab576148ab615a3a565b602002602001015191506148d2825f01518360600151846040015185602001518c8e61346d565b8160200151826040015183606001516148eb91906155d4565b6148f591906155d4565b6148ff9084615706565b9250600101614890565b508115614a35575f614919611433565b6040516370a0823160e01b81526001600160a01b0387811660048301529192505f918316906370a0823190602401602060405180830381865afa158015614962573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061498691906155a9565b905083811015614a1e5780156149b7576149aa6001600160a01b0383168b836139d7565b6149b48185615706565b93505b6149bf6127c9565b6001600160a01b031663f3fef3a38b866040518363ffffffff1660e01b81526004016149ec929190615a4e565b5f604051808303815f87803b158015614a03575f80fd5b505af1158015614a15573d5f803e3d5ffd5b50505050614a32565b614a326001600160a01b0383168b866139d7565b50505b866001600160a01b0316886001600160a01b03168a6001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db8989604051614a8d929190918252602082015260400190565b60405180910390a4505050505050505050565b5f81815260018301602052604081205480156140e4575f614ac2600183615706565b85549091505f90614ad590600190615706565b9050808214614b34575f865f018281548110614af357614af3615a3a565b905f5260205f200154905080875f018481548110614b1357614b13615a3a565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614b4557614b45615d8c565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610e18565b5f6002826003811115614b8f57614b8f6152d7565b614b999190615e5e565b60ff166001149050919050565b5f818152600183016020526040812054614beb57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610e18565b505f610e18565b5f80516020615f2c8339815191528054614c17906001600160a01b0316863086614e6d565b614c218483614ea6565b836001600160a01b0316856001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d78585604051614222929190918252602082015260400190565b5f836001600160a01b031663095ea7b38484604051602401614c92929190615a4e565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509050614ccb8482614eda565b61123a57614cf584856001600160a01b031663095ea7b3865f6040516024016139fd929190615a4e565b61123a8482614664565b805115614d0f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b606061108d83835f614f77565b614d3d6146c5565b61470e615006565b61470e6146c5565b614d556146c5565b614d5e84612e5f565b614d6783612b70565b614d70826131c9565b61123a81612f54565b614d816146c5565b5f80516020615ecc8339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03614dba8482615c92565b506004810161123a8382615c92565b614dd16146c5565b5f80516020615f2c8339815191525f80614dea8461500e565b9150915081614dfa576012614dfc565b805b83546001600160a81b031916600160a01b60ff92909216919091026001600160a01b031916176001600160a01b0394909416939093179091555050565b6001600160a01b038216614e6257604051634b637e8f60e11b81525f6004820152602401611046565b610e7c825f83614232565b6040516001600160a01b03848116602483015283811660448301526064820183905261123a9186918216906323b872dd906084016139fd565b6001600160a01b038216614ecf5760405163ec442f0560e01b81525f6004820152602401611046565b610e7c5f8383614232565b5f805f846001600160a01b031684604051614ef59190615d4d565b5f604051808303815f865af19150503d805f8114614f2e576040519150601f19603f3d011682016040523d82523d5f602084013e614f33565b606091505b5091509150818015614f5d575080511580614f5d575080806020019051810190614f5d9190615e43565b801561390f5750505050506001600160a01b03163b151590565b606081471015614f9c5760405163cd78605960e01b8152306004820152602401611046565b5f80856001600160a01b03168486604051614fb79190615d4d565b5f6040518083038185875af1925050503d805f8114614ff1576040519150601f19603f3d011682016040523d82523d5f602084013e614ff6565b606091505b5091509150614144868383614608565b6132a76146c5565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290515f918291829182916001600160a01b0387169161505491615d4d565b5f60405180830381855afa9150503d805f811461508c576040519150601f19603f3d011682016040523d82523d5f602084013e615091565b606091505b50915091508180156150a557506020815110155b156150d8575f818060200190518101906150bf91906155a9565b905060ff81116150d6576001969095509350505050565b505b505f9485945092505050565b5080546150f0906155e7565b5f825580601f106150ff575050565b601f0160209004905f5260205f209081019061199f91905b8082111561512a575f8155600101615117565b5090565b5f6020828403121561513e575f80fd5b81356001600160e01b03198116811461108d575f80fd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61108d6020830184615155565b5f602082840312156151a5575f80fd5b5035919050565b6001600160a01b038116811461199f575f80fd5b5f80604083850312156151d1575f80fd5b82356151dc816151ac565b946020939093013593505050565b80356003811061104f575f80fd5b5f60208284031215615208575f80fd5b61108d826151ea565b5f805f60608486031215615223575f80fd5b833561522e816151ac565b9250602084013561523e816151ac565b929592945050506040919091013590565b5f6020828403121561525f575f80fd5b813561108d816151ac565b5f805f6060848603121561527c575f80fd5b83359250602084013561528e816151ac565b9150604084013561529e816151ac565b809150509250925092565b5f80604083850312156152ba575f80fd5b8235915060208301356152cc816151ac565b809150509250929050565b634e487b7160e01b5f52602160045260245ffd5b6003811061530757634e487b7160e01b5f52602160045260245ffd5b9052565b602081525f60018060a01b038084511660208401528060208501511660408401528060408501511660608401525060608301516080830152608083015161535560a08401826152eb565b5060a083015160c08084015261168d60e0840182615155565b801515811461199f575f80fd5b5f6020828403121561538b575f80fd5b813561108d8161536e565b5f80604083850312156153a7575f80fd5b82356153b2816151ac565b91506153c0602084016151ea565b90509250929050565b5f80604083850312156153da575f80fd5b8235915060208301356001600160401b038111156153f6575f80fd5b830160c081860312156152cc575f80fd5b5f8060408385031215615418575f80fd5b50508035926020909101359150565b5f805f60408486031215615439575f80fd5b8335925060208401356001600160401b0380821115615456575f80fd5b818601915086601f830112615469575f80fd5b813581811115615477575f80fd5b876020828501011115615488575f80fd5b6020830194508093505050509250925092565b5f6060820190506154ad8284516152eb565b6020838101516001600160a01b039081169184019190915260409384015116929091019190915290565b5f602082840312156154e7575f80fd5b81356001600160401b038111156154fc575f80fd5b82016101e0818503121561108d575f80fd5b5f806040838503121561551f575f80fd5b823561552a816151ac565b915060208301356152cc8161536e565b602080825282518282018190525f9190848201906040850190845b8181101561557157835183529284019291840191600101615555565b50909695505050505050565b5f806040838503121561558e575f80fd5b8235615599816151ac565b915060208301356152cc816151ac565b5f602082840312156155b9575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610e1857610e186155c0565b600181811c908216806155fb57607f821691505b60208210810361561957634e487b7160e01b5f52602260045260245ffd5b50919050565b6020808252602a908201527f4d756c74695661756c743a20616461707465722063616e6e6f74206265207a65604082015269726f206164647265737360b01b606082015260800190565b60ff8181168382160190811115610e1857610e186155c0565b5f60208284031215615692575f80fd5b815161108d816151ac565b60208082526043908201527f4d756c74695661756c743a2064656661756c7420636f6c6c61746572616c206160408201527f7373657420646f6573206e6f74206d6174636820746865207661756c742061736060820152621cd95d60ea1b608082015260a00190565b81810381811115610e1857610e186155c0565b5f808335601e1984360301811261572e575f80fd5b8301803591506001600160401b03821115615747575f80fd5b60200191503681900382131561575b575f80fd5b9250929050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f61168d602083018486615762565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b03811182821017156157d3576157d361579d565b60405290565b604051601f8201601f191681016001600160401b03811182821017156158015761580161579d565b604052919050565b5f82601f830112615818575f80fd5b81356001600160401b038111156158315761583161579d565b615844601f8201601f19166020016157d9565b818152846020838601011115615858575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60c08236031215615884575f80fd5b60405160c081016001600160401b0382821081831117156158a7576158a761579d565b81604052843591506158b8826151ac565b9082526020840135906158ca826151ac565b816020840152604085013591506158e0826151ac565b816040840152606085013560608401526158fc608086016151ea565b608084015260a0850135915080821115615914575f80fd5b5061592136828601615809565b60a08301525092915050565b5f6001600160401b038211156159455761594561579d565b5060051b60200190565b5f61596161595c8461592d565b6157d9565b8381529050602080820190600785901b84018681111561597f575f80fd5b845b818110156159eb57608080828a031215615999575f80fd5b60408051918201916001600160401b03831181841017156159bc576159bc61579d565b918152825182528483015185830152828101519082015260608083015190820152845292820192608001615981565b505050509392505050565b5f60208284031215615a06575f80fd5b81516001600160401b03811115615a1b575f80fd5b8201601f81018413615a2b575f80fd5b61168d8482516020840161594f565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b03929092168252602082015260400190565b604080825283518282018190525f9190606090818501906020808901865b83811015615abc57815180518652838101518487015287810151888701528601518686015260809094019390820190600101615a85565b505050508093505050508260208301529392505050565b6001600160a01b03851681526060602082018190525f90615af79083018587615762565b8281036040840152615b098185615155565b979650505050505050565b600181815b80851115615b4e57815f1904821115615b3457615b346155c0565b80851615615b4157918102915b93841c9390800290615b19565b509250929050565b5f82615b6457506001610e18565b81615b7057505f610e18565b8160018114615b865760028114615b9057615bac565b6001915050610e18565b60ff841115615ba157615ba16155c0565b50506001821b610e18565b5060208310610133831016604e8410600b8410161715615bcf575081810a610e18565b615bd98383615b14565b805f1904821115615bec57615bec6155c0565b029392505050565b5f61108d60ff841683615b56565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b038416815260608101615c4060208301856152eb565b826040830152949350505050565b601f8211156112bd57805f5260205f20601f840160051c81016020851015615c735750805b601f840160051c820191505b8181101561422b575f8155600101615c7f565b81516001600160401b03811115615cab57615cab61579d565b615cbf81615cb984546155e7565b84615c4e565b602080601f831160018114615cf2575f8415615cdb5750858301515b5f19600386901b1c1916600185901b17855561454e565b5f85815260208120601f198616915b82811015615d2057888601518255948401946001909101908401615d01565b5085821015615d3d57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52601260045260245ffd5b5f81615d8557615d856155c0565b505f190190565b634e487b7160e01b5f52603160045260245ffd5b5f6020808385031215615db1575f80fd5b82516001600160401b03811115615dc6575f80fd5b8301601f81018513615dd6575f80fd5b8051615de461595c8261592d565b81815260069190911b82018301908381019087831115615e02575f80fd5b928401925b82841015615b095760408489031215615e1e575f80fd5b615e266157b1565b845181528585015186820152825260409093019290840190615e07565b5f60208284031215615e53575f80fd5b815161108d8161536e565b5f60ff831680615e7c57634e487b7160e01b5f52601260045260245ffd5b8060ff8416069150509291505056fe2e704739166abb26e88a93c0d60bae654bea582d8d8fa53cd8580ca0878fb548c1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e8237170593200052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00f2e7ea90350f51c371470888fa66eef25113ab1613606afc7de17fa5ee9605d202dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268000773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e009b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212206bc4c737b803efc80afd60f2e6bb5df4e5dc12079745baf36d53d70eb070c9be64736f6c63430008190033

Verified Source Code Full Match

Compiler: v0.8.25+commit.b61c2a91 EVM: cancun Optimization: Yes (200 runs)
MultiVault.sol 375 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../interfaces/vaults/IMultiVault.sol";
import {ERC4626Vault} from "./ERC4626Vault.sol";
import {MultiVaultStorage} from "./MultiVaultStorage.sol";
import {VaultControlStorage} from "./VaultControlStorage.sol";

contract MultiVault is IMultiVault, ERC4626Vault, MultiVaultStorage {
    using SafeERC20 for IERC20;
    using Math for uint256;

    uint256 private constant D6 = 1e6;
    bytes32 public constant ADD_SUBVAULT_ROLE = keccak256("ADD_SUBVAULT_ROLE");
    bytes32 public constant REMOVE_SUBVAULT_ROLE = keccak256("REMOVE_SUBVAULT_ROLE");
    bytes32 public constant SET_STRATEGY_ROLE = keccak256("SET_STRATEGY_ROLE");
    bytes32 public constant SET_FARM_ROLE = keccak256("SET_FARM_ROLE");
    bytes32 public constant REBALANCE_ROLE = keccak256("REBALANCE_ROLE");
    bytes32 public constant SET_DEFAULT_COLLATERAL_ROLE = keccak256("SET_DEFAULT_COLLATERAL_ROLE");
    bytes32 public constant SET_ADAPTER_ROLE = keccak256("SET_ADAPTER_ROLE");

    constructor(bytes32 name_, uint256 version_)
        VaultControlStorage(name_, version_)
        MultiVaultStorage(name_, version_)
    {
        _disableInitializers();
    }

    // ------------------------------- EXTERNAL VIEW FUNCTIONS -------------------------------

    /// @inheritdoc IERC4626
    function totalAssets()
        public
        view
        virtual
        override(IERC4626, ERC4626Upgradeable)
        returns (uint256 assets_)
    {
        address this_ = address(this);
        assets_ = IERC20(asset()).balanceOf(this_);
        IDefaultCollateral collateral = defaultCollateral();
        if (address(collateral) != address(0)) {
            assets_ += collateral.balanceOf(this_);
        }

        uint256 length = subvaultsCount();
        Subvault memory subvault;
        for (uint256 i = 0; i < length; i++) {
            subvault = subvaultAt(i);
            assets_ += adapterOf(subvault.protocol).stakedAt(subvault.vault);
            if (subvault.withdrawalQueue != address(0)) {
                assets_ += IWithdrawalQueue(subvault.withdrawalQueue).claimableAssetsOf(this_)
                    + IWithdrawalQueue(subvault.withdrawalQueue).pendingAssetsOf(this_);
            }
        }
    }

    // ------------------------------- EXTERNAL MUTATIVE FUNCTIONS -------------------------------

    /// @inheritdoc IMultiVault
    function initialize(InitParams calldata initParams) public virtual reinitializer(2) {
        __initializeERC4626(
            initParams.admin,
            initParams.limit,
            initParams.depositPause,
            initParams.withdrawalPause,
            initParams.depositWhitelist,
            initParams.asset,
            initParams.name,
            initParams.symbol
        );
        __initializeMultiVaultStorage(
            initParams.depositStrategy,
            initParams.withdrawalStrategy,
            initParams.rebalanceStrategy,
            initParams.defaultCollateral,
            initParams.symbioticAdapter,
            initParams.eigenLayerAdapter,
            initParams.erc4626Adapter
        );
        require(
            initParams.defaultCollateral == address(0)
                || IDefaultCollateral(initParams.defaultCollateral).asset() == initParams.asset,
            "MultiVault: default collateral asset does not match the vault asset"
        );
    }

    /// @inheritdoc IMultiVault
    function addSubvault(address vault, Protocol protocol) external onlyRole(ADD_SUBVAULT_ROLE) {
        IProtocolAdapter adapter = adapterOf(protocol);
        require(
            adapter.assetOf(vault) == asset(),
            "MultiVault: subvault asset does not match the vault asset"
        );
        _addSubvault(vault, adapter.handleVault(vault), protocol);
    }

    /// @inheritdoc IMultiVault
    function removeSubvault(address subvault) external onlyRole(REMOVE_SUBVAULT_ROLE) {
        _removeSubvault(subvault);
    }

    /// @inheritdoc IMultiVault
    function setDepositStrategy(address newDepositStrategy) external onlyRole(SET_STRATEGY_ROLE) {
        require(
            newDepositStrategy != address(0), "MultiVault: deposit strategy cannot be zero address"
        );
        _setDepositStrategy(newDepositStrategy);
    }

    /// @inheritdoc IMultiVault
    function setWithdrawalStrategy(address newWithdrawalStrategy)
        external
        onlyRole(SET_STRATEGY_ROLE)
    {
        require(
            newWithdrawalStrategy != address(0),
            "MultiVault: withdrawal strategy cannot be zero address"
        );
        _setWithdrawalStrategy(newWithdrawalStrategy);
    }

    /// @inheritdoc IMultiVault
    function setRebalanceStrategy(address newRebalanceStrategy)
        external
        onlyRole(SET_STRATEGY_ROLE)
    {
        require(
            newRebalanceStrategy != address(0),
            "MultiVault: rebalance strategy cannot be zero address"
        );
        _setRebalanceStrategy(newRebalanceStrategy);
    }

    /// @inheritdoc IMultiVault
    function setDefaultCollateral(address defaultCollateral_)
        external
        onlyRole(SET_DEFAULT_COLLATERAL_ROLE)
    {
        require(
            address(defaultCollateral()) == address(0) && defaultCollateral_ != address(0),
            "MultiVault: default collateral already set or cannot be zero address"
        );
        require(
            IDefaultCollateral(defaultCollateral_).asset() == asset(),
            "MultiVault: default collateral asset does not match the vault asset"
        );
        _setDefaultCollateral(defaultCollateral_);
    }

    /// @inheritdoc IMultiVault
    function setSymbioticAdapter(address adapter_) external onlyRole(SET_ADAPTER_ROLE) {
        require(adapter_ != address(0), "MultiVault: adapter cannot be zero address");
        _setSymbioticAdapter(adapter_);
    }

    /// @inheritdoc IMultiVault
    function setEigenLayerAdapter(address adapter_) external onlyRole(SET_ADAPTER_ROLE) {
        require(adapter_ != address(0), "MultiVault: adapter cannot be zero address");
        _setEigenLayerAdapter(adapter_);
    }

    /// @inheritdoc IMultiVault
    function setERC4626Adapter(address adapter_) external onlyRole(SET_ADAPTER_ROLE) {
        require(adapter_ != address(0), "MultiVault: adapter cannot be zero address");
        _setERC4626Adapter(adapter_);
    }

    /// @inheritdoc IMultiVault
    function setRewardsData(uint256 farmId, RewardData calldata rewardData)
        external
        onlyRole(SET_FARM_ROLE)
    {
        if (rewardData.token != address(0)) {
            require(
                rewardData.token != asset() && rewardData.token != address(defaultCollateral()),
                "MultiVault: reward token cannot be the same as the asset or default collateral"
            );
            require(rewardData.curatorFeeD6 <= D6, "MultiVault: curator fee exceeds 100%");
            require(
                rewardData.distributionFarm != address(0),
                "MultiVault: distribution farm address cannot be zero"
            );
            if (rewardData.curatorFeeD6 != 0) {
                require(
                    rewardData.curatorTreasury != address(0),
                    "MultiVault: curator treasury address cannot be zero when fee is set"
                );
            }
            adapterOf(rewardData.protocol).validateRewardData(rewardData.data);
        }
        _setRewardData(farmId, rewardData);
    }

    /// @inheritdoc IMultiVault
    function rebalance() external onlyRole(REBALANCE_ROLE) {
        address this_ = address(this);
        IRebalanceStrategy.RebalanceData[] memory data =
            rebalanceStrategy().calculateRebalanceAmounts(this_);
        for (uint256 i = 0; i < data.length; i++) {
            _withdraw(data[i].subvaultIndex, data[i].staked, 0, data[i].claimable, this_, this_);
        }
        IDefaultCollateral collateral = defaultCollateral();
        if (address(collateral) != address(0)) {
            uint256 balance = collateral.balanceOf(this_);
            if (balance != 0) {
                collateral.withdraw(this_, balance);
            }
        }
        for (uint256 i = 0; i < data.length; i++) {
            _deposit(data[i].subvaultIndex, data[i].deposit);
        }
        _depositIntoCollateral();
        emit Rebalance(data, block.timestamp);
    }

    /// @inheritdoc IMultiVault
    function pushRewards(uint256 farmId, bytes calldata farmData) external nonReentrant {
        require(farmIdsContains(farmId), "MultiVault: farm not found");
        IMultiVaultStorage.RewardData memory data = rewardData(farmId);
        IERC20 rewardToken = IERC20(data.token);

        address this_ = address(this);
        uint256 rewardAmount = rewardToken.balanceOf(this_);

        Address.functionDelegateCall(
            address(adapterOf(data.protocol)),
            abi.encodeCall(
                IProtocolAdapter.pushRewards, (address(rewardToken), farmData, data.data)
            )
        );

        rewardAmount = rewardToken.balanceOf(this_) - rewardAmount;
        if (rewardAmount == 0) {
            return;
        }

        uint256 curatorFee = rewardAmount.mulDiv(data.curatorFeeD6, D6);
        if (curatorFee != 0) {
            rewardToken.safeTransfer(data.curatorTreasury, curatorFee);
        }
        rewardAmount = rewardAmount - curatorFee;
        if (rewardAmount != 0) {
            rewardToken.safeTransfer(data.distributionFarm, rewardAmount);
        }
        emit RewardsPushed(farmId, rewardAmount, curatorFee, block.timestamp);
    }

    // ------------------------------- INTERNAL MUTATIVE FUNCTIONS -------------------------------

    /// @dev Deposits assets into the specified subvault
    function _deposit(uint256 subvaultIndex, uint256 assets) private {
        if (assets == 0) {
            return;
        }
        Subvault memory subvault = subvaultAt(subvaultIndex);
        Address.functionDelegateCall(
            address(adapterOf(subvault.protocol)),
            abi.encodeCall(IProtocolAdapter.deposit, (subvault.vault, assets))
        );
    }

    /// @dev Withdraws assets from the specified subvault
    function _withdraw(
        uint256 subvaultIndex,
        uint256 request,
        uint256 pending,
        uint256 claimable,
        address owner,
        address receiver
    ) private {
        Subvault memory subvault = subvaultAt(subvaultIndex);
        address this_ = address(this);
        if (claimable != 0) {
            IWithdrawalQueue(subvault.withdrawalQueue).claim(this_, receiver, claimable);
        }
        if (pending != 0) {
            IWithdrawalQueue(subvault.withdrawalQueue).transferPendingAssets(receiver, pending);
        }
        if (request != 0) {
            Address.functionDelegateCall(
                address(adapterOf(subvault.protocol)),
                abi.encodeCall(
                    IProtocolAdapter.withdraw,
                    (subvault.vault, subvault.withdrawalQueue, receiver, request, owner)
                )
            );
        }
    }

    /// @dev Deposits assets into the default collateral
    function _depositIntoCollateral() private {
        IDefaultCollateral collateral = defaultCollateral();
        if (address(collateral) == address(0)) {
            return;
        }
        uint256 limit_ = collateral.limit();
        uint256 supply_ = collateral.totalSupply();
        if (supply_ >= limit_) {
            return;
        }
        address this_ = address(this);
        IERC20 asset_ = IERC20(asset());
        uint256 assets = asset_.balanceOf(this_).min(limit_ - supply_);
        if (assets == 0) {
            return;
        }
        asset_.safeIncreaseAllowance(address(collateral), assets);
        collateral.deposit(this_, assets);
        emit DepositIntoCollateral(assets);
    }

    function _deposit(address caller, address receiver, uint256 assets, uint256 shares)
        internal
        virtual
        override
    {
        address this_ = address(this);
        IDepositStrategy.DepositData[] memory data =
            depositStrategy().calculateDepositAmounts(this_, assets);
        super._deposit(caller, receiver, assets, shares);
        for (uint256 i = 0; i < data.length; i++) {
            if (data[i].deposit != 0) {
                _deposit(data[i].subvaultIndex, data[i].deposit);
                assets -= data[i].deposit;
            }
        }

        _depositIntoCollateral();
    }

    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual override {
        address this_ = address(this);

        IWithdrawalStrategy.WithdrawalData[] memory data =
            withdrawalStrategy().calculateWithdrawalAmounts(this_, assets);

        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        _burn(owner, shares);

        uint256 liquid = assets;
        IWithdrawalStrategy.WithdrawalData memory d;
        for (uint256 i = 0; i < data.length; i++) {
            d = data[i];
            _withdraw(d.subvaultIndex, d.staked, d.pending, d.claimable, owner, receiver);
            liquid -= d.staked + d.pending + d.claimable;
        }

        if (liquid != 0) {
            IERC20 asset_ = IERC20(asset());
            uint256 balance = asset_.balanceOf(this_);
            if (balance < liquid) {
                if (balance != 0) {
                    asset_.safeTransfer(receiver, balance);
                    liquid -= balance;
                }
                defaultCollateral().withdraw(receiver, liquid);
            } else {
                asset_.safeTransfer(receiver, liquid);
            }
        }

        // emitting event with transferred + new pending assets
        emit Withdraw(caller, receiver, owner, assets, shares);
    }
}
IMultiVault.sol 61 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "./IMultiVaultStorage.sol";

interface IMultiVault is IMultiVaultStorage {
    struct InitParams {
        address admin;
        uint256 limit;
        bool depositPause;
        bool withdrawalPause;
        bool depositWhitelist;
        address asset;
        string name;
        string symbol;
        address depositStrategy;
        address withdrawalStrategy;
        address rebalanceStrategy;
        address defaultCollateral;
        address symbioticAdapter;
        address eigenLayerAdapter;
        address erc4626Adapter;
    }

    function initialize(InitParams calldata initParams) external;

    function rebalance() external;

    function addSubvault(address vault, Protocol protocol) external;

    function removeSubvault(address subvault) external;

    function setDepositStrategy(address newDepositStrategy) external;

    function setWithdrawalStrategy(address newWithdrawalStrategy) external;

    function setRebalanceStrategy(address newRebalanceStrategy) external;

    function setRewardsData(uint256 farmId, RewardData calldata rewardData) external;

    function setDefaultCollateral(address defaultCollateral_) external;

    function setSymbioticAdapter(address adapter_) external;

    function setEigenLayerAdapter(address adapter_) external;

    function setERC4626Adapter(address adapter_) external;

    function pushRewards(uint256 farmId, bytes calldata data) external;

    event Rebalance(IRebalanceStrategy.RebalanceData[] data, uint256 timestamp);

    event DepositIntoCollateral(uint256 assets);

    event RewardsPushed(
        uint256 indexed farmId,
        uint256 indexed rewardAmount,
        uint256 indexed curatorFee,
        uint256 timestamp
    );
}
ERC4626Vault.sol 161 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../interfaces/vaults/IERC4626Vault.sol";
import {VaultControl} from "./VaultControl.sol";

abstract contract ERC4626Vault is VaultControl, ERC4626Upgradeable, IERC4626Vault {
    bytes32[16] private _reserved; // Reserved storage space for backward compatibility.

    /**
     * @notice Initializes the ERC4626 vault with the provided settings, including admin, limits, pause states, and token details.
     * @param _admin The address of the admin to be granted control over the vault.
     * @param _limit The initial deposit limit for the vault.
     * @param _depositPause The initial state of the `depositPause` flag.
     * @param _withdrawalPause The initial state of the `withdrawalPause` flag.
     * @param _depositWhitelist The initial state of the `depositWhitelist` flag.
     * @param _asset The address of the underlying ERC20 asset for the ERC4626 vault.
     * @param _name The name of the ERC20 token representing shares of the vault.
     * @param _symbol The symbol of the ERC20 token representing shares of the vault.
     *
     * @custom:effects
     * - Initializes the vault control settings, including admin, limits, and pause states, via `__initializeVaultControl`.
     * - Initializes the ERC20 token properties with the provided `_name` and `_symbol`.
     * - Initializes the ERC4626 vault with the provided underlying asset (`_asset`).
     *
     * @dev This function is protected by the `onlyInitializing` modifier, ensuring it is only called during the initialization phase of the contract.
     */
    function __initializeERC4626(
        address _admin,
        uint256 _limit,
        bool _depositPause,
        bool _withdrawalPause,
        bool _depositWhitelist,
        address _asset,
        string memory _name,
        string memory _symbol
    ) internal onlyInitializing {
        __initializeVaultControl(_admin, _limit, _depositPause, _withdrawalPause, _depositWhitelist);
        __ERC20_init(_name, _symbol);
        __ERC4626_init(IERC20(_asset));
    }

    /// @inheritdoc IERC4626
    function maxMint(address account)
        public
        view
        virtual
        override(ERC4626Upgradeable, IERC4626)
        returns (uint256)
    {
        uint256 assets = maxDeposit(account);
        if (assets == type(uint256).max) {
            return type(uint256).max;
        }
        return convertToShares(assets);
    }

    /// @inheritdoc IERC4626
    function maxDeposit(address account)
        public
        view
        virtual
        override(ERC4626Upgradeable, IERC4626)
        returns (uint256)
    {
        if (depositPause()) {
            return 0;
        }
        if (depositWhitelist() && !isDepositorWhitelisted(account)) {
            return 0;
        }
        uint256 limit_ = limit();
        if (limit_ == type(uint256).max) {
            return type(uint256).max;
        }
        uint256 assets_ = totalAssets();
        return limit_ >= assets_ ? limit_ - assets_ : 0;
    }

    /// @inheritdoc IERC4626
    function maxWithdraw(address account)
        public
        view
        virtual
        override(ERC4626Upgradeable, IERC4626)
        returns (uint256)
    {
        if (withdrawalPause()) {
            return 0;
        }
        return super.maxWithdraw(account);
    }

    /// @inheritdoc IERC4626
    function maxRedeem(address account)
        public
        view
        virtual
        override(ERC4626Upgradeable, IERC4626)
        returns (uint256)
    {
        if (withdrawalPause()) {
            return 0;
        }
        return super.maxRedeem(account);
    }

    /// @inheritdoc IERC4626Vault
    function deposit(uint256 assets, address receiver, address referral)
        public
        virtual
        returns (uint256 shares)
    {
        shares = deposit(assets, receiver);
        emit ReferralDeposit(assets, receiver, referral);
    }

    /// @inheritdoc IERC4626
    function deposit(uint256 assets, address receiver)
        public
        virtual
        override(ERC4626Upgradeable, IERC4626)
        nonReentrant
        returns (uint256)
    {
        return super.deposit(assets, receiver);
    }

    /// @inheritdoc IERC4626
    function mint(uint256 shares, address receiver)
        public
        virtual
        override(ERC4626Upgradeable, IERC4626)
        nonReentrant
        returns (uint256)
    {
        return super.mint(shares, receiver);
    }

    /// @inheritdoc IERC4626
    function withdraw(uint256 assets, address receiver, address owner)
        public
        virtual
        override(ERC4626Upgradeable, IERC4626)
        nonReentrant
        returns (uint256)
    {
        return super.withdraw(assets, receiver, owner);
    }

    /// @inheritdoc IERC4626
    function redeem(uint256 shares, address receiver, address owner)
        public
        virtual
        override(ERC4626Upgradeable, IERC4626)
        nonReentrant
        returns (uint256)
    {
        return super.redeem(shares, receiver, owner);
    }
}
MultiVaultStorage.sol 215 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../interfaces/vaults/IMultiVaultStorage.sol";

contract MultiVaultStorage is IMultiVaultStorage, Initializable {
    using EnumerableSet for EnumerableSet.UintSet;

    bytes32 private immutable storageSlotRef;

    constructor(bytes32 name_, uint256 version_) {
        storageSlotRef = keccak256(
            abi.encode(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            "mellow.simple-lrt.storage.MultiVaultStorage", name_, version_
                        )
                    )
                ) - 1
            )
        ) & ~bytes32(uint256(0xff));
    }

    function _multiStorage() private view returns (MultiStorage storage $) {
        bytes32 slot = storageSlotRef;
        assembly {
            $.slot := slot
        }
    }

    /// ------------------------------- EXTERNAL VIEW FUNCTIONS -------------------------------

    /// @inheritdoc IMultiVaultStorage
    function subvaultsCount() public view returns (uint256) {
        return _multiStorage().subvaults.length;
    }

    /// @inheritdoc IMultiVaultStorage
    function subvaultAt(uint256 index) public view returns (Subvault memory) {
        return _multiStorage().subvaults[index];
    }

    /// @inheritdoc IMultiVaultStorage
    function indexOfSubvault(address subvault) public view returns (uint256) {
        return _multiStorage().indexOfSubvault[subvault];
    }

    /// @inheritdoc IMultiVaultStorage
    function defaultCollateral() public view returns (IDefaultCollateral) {
        return IDefaultCollateral(_multiStorage().defaultCollateral);
    }

    /// @inheritdoc IMultiVaultStorage
    function depositStrategy() public view returns (IDepositStrategy) {
        return IDepositStrategy(_multiStorage().depositStrategy);
    }

    /// @inheritdoc IMultiVaultStorage
    function withdrawalStrategy() public view returns (IWithdrawalStrategy) {
        return IWithdrawalStrategy(_multiStorage().withdrawalStrategy);
    }

    /// @inheritdoc IMultiVaultStorage
    function rebalanceStrategy() public view returns (IRebalanceStrategy) {
        return IRebalanceStrategy(_multiStorage().rebalanceStrategy);
    }

    /// @inheritdoc IMultiVaultStorage
    function symbioticAdapter() public view returns (IProtocolAdapter) {
        return IProtocolAdapter(_multiStorage().symbioticAdapter);
    }

    /// @inheritdoc IMultiVaultStorage
    function eigenLayerAdapter() public view returns (IProtocolAdapter) {
        return IProtocolAdapter(_multiStorage().eigenLayerAdapter);
    }

    /// @inheritdoc IMultiVaultStorage
    function erc4626Adapter() public view returns (IProtocolAdapter) {
        return IProtocolAdapter(_multiStorage().erc4626Adapter);
    }

    /// @inheritdoc IMultiVaultStorage
    function rewardData(uint256 farmId) public view returns (RewardData memory) {
        return _multiStorage().rewardData[farmId];
    }

    /// @inheritdoc IMultiVaultStorage
    function farmIds() public view returns (uint256[] memory) {
        return _multiStorage().farmIds.values();
    }

    /// @inheritdoc IMultiVaultStorage
    function farmCount() public view returns (uint256) {
        return _multiStorage().farmIds.length();
    }

    /// @inheritdoc IMultiVaultStorage
    function farmIdAt(uint256 index) public view returns (uint256) {
        return _multiStorage().farmIds.at(index);
    }

    /// @inheritdoc IMultiVaultStorage
    function farmIdsContains(uint256 farmId) public view returns (bool) {
        return _multiStorage().farmIds.contains(farmId);
    }

    /// @inheritdoc IMultiVaultStorage
    function adapterOf(Protocol protocol) public view returns (IProtocolAdapter adapter) {
        if (protocol == Protocol.SYMBIOTIC) {
            adapter = symbioticAdapter();
        } else if (protocol == Protocol.EIGEN_LAYER) {
            adapter = eigenLayerAdapter();
        } else if (protocol == Protocol.ERC4626) {
            adapter = erc4626Adapter();
        }
        require(address(adapter) != address(0), "MultiVault: unsupported protocol");
    }

    /// ------------------------------- INTERNAL MUTATIVE FUNCTIONS -------------------------------

    function __initializeMultiVaultStorage(
        address depositStrategy_,
        address withdrawalStrategy_,
        address rebalanceStrategy_,
        address defaultCollateral_,
        address symbioticAdapter_,
        address eigenLayerAdapter_,
        address erc4626Adapter_
    ) internal onlyInitializing {
        _setDepositStrategy(depositStrategy_);
        _setWithdrawalStrategy(withdrawalStrategy_);
        _setRebalanceStrategy(rebalanceStrategy_);
        _setDefaultCollateral(defaultCollateral_);
        _setSymbioticAdapter(symbioticAdapter_);
        _setEigenLayerAdapter(eigenLayerAdapter_);
        _setERC4626Adapter(erc4626Adapter_);
    }

    function _setSymbioticAdapter(address symbioticAdapter_) internal {
        _multiStorage().symbioticAdapter = symbioticAdapter_;
        emit SymbioticAdapterSet(symbioticAdapter_);
    }

    function _setEigenLayerAdapter(address eigenLayerAdapter_) internal {
        _multiStorage().eigenLayerAdapter = eigenLayerAdapter_;
        emit EigenLayerAdapterSet(eigenLayerAdapter_);
    }

    function _setERC4626Adapter(address erc4626Adapter_) internal {
        _multiStorage().erc4626Adapter = erc4626Adapter_;
        emit ERC4626AdapterSet(erc4626Adapter_);
    }

    function _setDepositStrategy(address newDepositStrategy) internal {
        _multiStorage().depositStrategy = newDepositStrategy;
        emit DepositStrategySet(newDepositStrategy);
    }

    function _setWithdrawalStrategy(address newWithdrawalStrategy) internal {
        _multiStorage().withdrawalStrategy = newWithdrawalStrategy;
        emit WithdrawalStrategySet(newWithdrawalStrategy);
    }

    function _setRebalanceStrategy(address newRebalanceStrategy) internal {
        _multiStorage().rebalanceStrategy = newRebalanceStrategy;
        emit RebalanceStrategySet(newRebalanceStrategy);
    }

    function _setDefaultCollateral(address defaultCollateral_) internal {
        _multiStorage().defaultCollateral = defaultCollateral_;
        emit DefaultCollateralSet(defaultCollateral_);
    }

    function _addSubvault(address vault, address withdrawalQueue, Protocol protocol) internal {
        MultiStorage storage $ = _multiStorage();
        require($.indexOfSubvault[vault] == 0, "MultiVaultStorage: subvault already exists");
        $.subvaults.push(Subvault(protocol, vault, withdrawalQueue));
        uint256 index = $.subvaults.length;
        $.indexOfSubvault[vault] = index;
        emit SubvaultAdded(vault, withdrawalQueue, protocol, index - 1);
    }

    function _removeSubvault(address vault) internal {
        MultiStorage storage $ = _multiStorage();
        uint256 index = $.indexOfSubvault[vault];
        require(index != 0, "MultiVaultStorage: subvault not found");
        index--;
        uint256 last = $.subvaults.length - 1;
        emit SubvaultRemoved(vault, index);
        if (index < last) {
            Subvault memory lastSubvault = $.subvaults[last];
            $.subvaults[index] = lastSubvault;
            $.indexOfSubvault[lastSubvault.vault] = index + 1;
            emit SubvaultIndexChanged(lastSubvault.vault, last, index);
        }
        $.subvaults.pop();
        delete $.indexOfSubvault[vault];
    }

    function _setRewardData(uint256 farmId, RewardData memory data) internal {
        MultiStorage storage $ = _multiStorage();
        if (data.token == address(0)) {
            if ($.farmIds.remove(farmId)) {
                delete $.rewardData[farmId];
                emit RewardDataRemoved(farmId);
            }
        } else {
            $.rewardData[farmId] = data;
            $.farmIds.add(farmId);
            emit RewardDataSet(farmId, data);
        }
    }
}
VaultControlStorage.sol 162 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../interfaces/vaults/IVaultControlStorage.sol";

abstract contract VaultControlStorage is IVaultControlStorage, Initializable, ContextUpgradeable {
    bytes32 private immutable storageSlotRef;

    constructor(bytes32 name_, uint256 version_) {
        storageSlotRef = keccak256(
            abi.encode(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            "mellow.simple-lrt.storage.VaultControlStorage", name_, version_
                        )
                    )
                ) - 1
            )
        ) & ~bytes32(uint256(0xff));
    }

    /**
     * @notice Initializes the Vault storage with the provided settings for limit, pause states, and whitelist.
     * @param _limit The initial value for the Vault's deposit limit.
     * @param _depositPause The initial state for the `depositPause` flag.
     * @param _withdrawalPause The initial state for the `withdrawalPause` flag.
     * @param _depositWhitelist The initial state for the `depositWhitelist` flag.
     *
     * @custom:requirements
     * - This function MUST not be called more than once; it is intended for one-time initialization.
     *
     * @custom:effects
     * - Sets the provided limit, pause states, and whitelist state in the Vault's storage.
     * - Emits the `LimitSet` event after the limit is set.
     * - Emits the `DepositPauseSet` event after the deposit pause state is set.
     * - Emits the `WithdrawalPauseSet` event after the withdrawal pause state is set.
     * - Emits the `DepositWhitelistSet` event after the deposit whitelist state is set.
     *
     * @dev This function is protected by the `onlyInitializing` modifier to ensure it is only called during the contract's initialization phase.
     */
    function __initializeVaultControlStorage(
        uint256 _limit,
        bool _depositPause,
        bool _withdrawalPause,
        bool _depositWhitelist
    ) internal onlyInitializing {
        _setLimit(_limit);
        _setDepositPause(_depositPause);
        _setWithdrawalPause(_withdrawalPause);
        _setDepositWhitelist(_depositWhitelist);
    }
    /// @inheritdoc IVaultControlStorage

    function depositPause() public view returns (bool) {
        return _vaultStorage().depositPause;
    }

    /// @inheritdoc IVaultControlStorage
    function withdrawalPause() public view returns (bool) {
        return _vaultStorage().withdrawalPause;
    }

    /// @inheritdoc IVaultControlStorage
    function limit() public view returns (uint256) {
        return _vaultStorage().limit;
    }

    /// @inheritdoc IVaultControlStorage
    function depositWhitelist() public view returns (bool) {
        return _vaultStorage().depositWhitelist;
    }

    /// @inheritdoc IVaultControlStorage
    function isDepositorWhitelisted(address account) public view returns (bool) {
        return _vaultStorage().isDepositorWhitelisted[account];
    }

    /**
     * @notice Sets a new `limit` for the Vault.
     * @param _limit The new limit for the Vault.
     *
     * @custom:effects
     * - Updates the Vault's `limit` in storage.
     * - Emits the `LimitSet` event with the new limit, current timestamp, and the caller's address.
     */
    function _setLimit(uint256 _limit) internal {
        Storage storage s = _vaultStorage();
        s.limit = _limit;
        emit LimitSet(_limit, block.timestamp, _msgSender());
    }

    /**
     * @notice Sets a new `depositPause` state for the Vault.
     * @param _paused The new value for the `depositPause` state.
     *
     * @custom:effects
     * - Updates the Vault's `depositPause` state in storage.
     * - Emits the `DepositPauseSet` event with the new pause state, current timestamp, and the caller's address.
     */
    function _setDepositPause(bool _paused) internal {
        Storage storage s = _vaultStorage();
        s.depositPause = _paused;
        emit DepositPauseSet(_paused, block.timestamp, _msgSender());
    }

    /**
     * @notice Sets a new `withdrawalPause` state for the Vault.
     * @param _paused The new value for the `withdrawalPause` state.
     *
     * @custom:effects
     * - Updates the Vault's `withdrawalPause` state in storage.
     * - Emits the `WithdrawalPauseSet` event with the new pause state, current timestamp, and the caller's address.
     */
    function _setWithdrawalPause(bool _paused) internal {
        Storage storage s = _vaultStorage();
        s.withdrawalPause = _paused;
        emit WithdrawalPauseSet(_paused, block.timestamp, _msgSender());
    }

    /**
     * @notice Sets a new `depositWhitelist` state for the Vault.
     * @param _status The new value for the `depositWhitelist` state.
     *
     * @custom:effects
     * - Updates the Vault's `depositWhitelist` state in storage.
     * - Emits the `DepositWhitelistSet` event with the new whitelist status, current timestamp, and the caller's address.
     */
    function _setDepositWhitelist(bool _status) internal {
        Storage storage s = _vaultStorage();
        s.depositWhitelist = _status;
        emit DepositWhitelistSet(_status, block.timestamp, _msgSender());
    }

    /**
     * @notice Sets a new whitelist `status` for the given `account`.
     * @param account The address of the account to update.
     * @param status The new whitelist status for the account.
     *
     * @custom:effects
     * - Updates the whitelist status of the `account` in storage.
     * - Emits the `DepositorWhitelistStatusSet` event with the account, new status, current timestamp, and the caller's address.
     */
    function _setDepositorWhitelistStatus(address account, bool status) internal {
        Storage storage s = _vaultStorage();
        s.isDepositorWhitelisted[account] = status;
        emit DepositorWhitelistStatusSet(account, status, block.timestamp, _msgSender());
    }

    /**
     * @notice Accesses the storage slot for the Vault's data.
     * @return $ A reference to the `Storage` struct for the Vault.
     *
     * @dev This function uses inline assembly to access a predefined storage slot.
     */
    function _vaultStorage() private view returns (Storage storage $) {
        bytes32 slot = storageSlotRef;
        assembly {
            $.slot := slot
        }
    }
}
IMultiVaultStorage.sol 114 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {IProtocolAdapter} from "../adapters/IProtocolAdapter.sol";
import {IWithdrawalQueue} from "../queues/IWithdrawalQueue.sol";
import {IDepositStrategy} from "../strategies/IDepositStrategy.sol";
import {IRebalanceStrategy} from "../strategies/IRebalanceStrategy.sol";
import {IWithdrawalStrategy} from "../strategies/IWithdrawalStrategy.sol";
import {IDefaultCollateral} from "../tokens/IDefaultCollateral.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {
    ERC4626Upgradeable,
    IERC4626
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

interface IMultiVaultStorage {
    enum Protocol {
        SYMBIOTIC,
        EIGEN_LAYER,
        ERC4626
    }

    struct Subvault {
        Protocol protocol;
        address vault;
        address withdrawalQueue;
    }

    struct RewardData {
        address distributionFarm;
        address curatorTreasury;
        address token;
        uint256 curatorFeeD6;
        Protocol protocol;
        bytes data;
    }

    struct MultiStorage {
        address depositStrategy;
        address withdrawalStrategy;
        address rebalanceStrategy;
        Subvault[] subvaults;
        mapping(address subvault => uint256 index) indexOfSubvault;
        mapping(uint256 id => RewardData) rewardData;
        EnumerableSet.UintSet farmIds;
        address defaultCollateral;
        address symbioticAdapter;
        address eigenLayerAdapter;
        address erc4626Adapter;
        bytes32[16] _gap;
    }

    function subvaultsCount() external view returns (uint256);

    function subvaultAt(uint256 index) external view returns (Subvault memory);

    function indexOfSubvault(address subvault) external view returns (uint256);

    function farmIds() external view returns (uint256[] memory);

    function farmCount() external view returns (uint256);

    function farmIdAt(uint256 index) external view returns (uint256);

    function farmIdsContains(uint256 farmId) external view returns (bool);

    function defaultCollateral() external view returns (IDefaultCollateral);

    function depositStrategy() external view returns (IDepositStrategy);

    function withdrawalStrategy() external view returns (IWithdrawalStrategy);

    function rebalanceStrategy() external view returns (IRebalanceStrategy);

    function eigenLayerAdapter() external view returns (IProtocolAdapter);

    function symbioticAdapter() external view returns (IProtocolAdapter);

    function erc4626Adapter() external view returns (IProtocolAdapter);

    function adapterOf(Protocol protocol) external view returns (IProtocolAdapter);

    function rewardData(uint256 farmId) external view returns (RewardData memory);

    event SubvaultAdded(
        address indexed subvault, address withdrawalQueue, Protocol protocol, uint256 subvaultIndex
    );

    event SubvaultRemoved(address indexed subvault, uint256 subvaultIndex);

    event SubvaultIndexChanged(address indexed subvault, uint256 oldIndex, uint256 newIndex);

    event RewardDataRemoved(uint256 indexed farmId);

    event RewardDataSet(uint256 indexed farmId, RewardData data);

    event DefaultCollateralSet(address indexed defaultCollateral);

    event DepositStrategySet(address indexed depositStrategy);

    event WithdrawalStrategySet(address indexed withdrawalStrategy);

    event RebalanceStrategySet(address indexed rebalanceStrategy);

    event SymbioticAdapterSet(address indexed symbioticAdapter);

    event EigenLayerAdapterSet(address indexed eigenLayerAdapter);

    event ERC4626AdapterSet(address indexed erc4626Adapter);
}
IERC4626Vault.sol 44 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {
    ERC4626Upgradeable,
    IERC4626
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title IERC4626Vault
 * @notice Extension of the IERC4626 interface that introduces a `deposit` method with an additional referral address parameter.
 * @dev This interface enhances the standard ERC4626 vault by adding referral-based deposits.
 * @dev Also extends the VaultControl interface for managing deposit limits, deposit pause and withdrawal pause.
 */
interface IERC4626Vault is IERC4626 {
    /**
     * @notice Emitted when a deposit is made through a referral.
     * @param assets The amount of underlying tokens deposited.
     * @param receiver The address receiving the vault shares.
     * @param referral The address of the referral involved in the deposit.
     */
    event ReferralDeposit(uint256 assets, address receiver, address referral);

    /**
     * @notice Mints vault shares to the `receiver` by depositing a specified amount of `assets` with an associated `referral`.
     * @param assets The amount of underlying tokens to be deposited.
     * @param receiver The address that will receive the vault shares.
     * @param referral The address of the referral associated with the deposit.
     * @return shares The amount of vault shares minted to the `receiver`.
     *
     * @custom:requirements
     * - The `assets` to be deposited MUST be greater than 0.
     *
     * @custom:effects
     * - Transfers the underlying tokens (`assets`) from the sender to the vault.
     * - Mints the corresponding `shares` to the `receiver`.
     * - Deposits the `assets` into the underlying bond.
     * - Emits a `ReferralDeposit` event.
     */
    function deposit(uint256 assets, address receiver, address referral)
        external
        returns (uint256 shares);
}
VaultControl.sol 90 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../interfaces/vaults/IVaultControl.sol";
import {VaultControlStorage} from "./VaultControlStorage.sol";

abstract contract VaultControl is
    IVaultControl,
    VaultControlStorage,
    ReentrancyGuardUpgradeable,
    AccessControlEnumerableUpgradeable
{
    bytes32 private constant SET_LIMIT_ROLE = keccak256("SET_LIMIT_ROLE");
    bytes32 private constant PAUSE_WITHDRAWALS_ROLE = keccak256("PAUSE_WITHDRAWALS_ROLE");
    bytes32 private constant UNPAUSE_WITHDRAWALS_ROLE = keccak256("UNPAUSE_WITHDRAWALS_ROLE");
    bytes32 private constant PAUSE_DEPOSITS_ROLE = keccak256("PAUSE_DEPOSITS_ROLE");
    bytes32 private constant UNPAUSE_DEPOSITS_ROLE = keccak256("UNPAUSE_DEPOSITS_ROLE");
    bytes32 private constant SET_DEPOSIT_WHITELIST_ROLE = keccak256("SET_DEPOSIT_WHITELIST_ROLE");
    bytes32 private constant SET_DEPOSITOR_WHITELIST_STATUS_ROLE =
        keccak256("SET_DEPOSITOR_WHITELIST_STATUS_ROLE");

    /**
     * @notice Initializes the vault control settings, including roles, limits, and pause states.
     * @param _admin The address of the admin who will be granted the `DEFAULT_ADMIN_ROLE`.
     * @param _limit The initial limit on deposits for the vault.
     * @param _depositPause A boolean indicating whether deposits should be paused initially.
     * @param _withdrawalPause A boolean indicating whether withdrawals should be paused initially.
     * @param _depositWhitelist A boolean indicating whether a deposit whitelist should be enabled initially.
     *
     * @dev This function performs the following steps:
     * - Initializes the reentrancy guard to prevent reentrancy attacks.
     * - Initializes the access control system, setting up roles and permissions.
     * - Grants the `DEFAULT_ADMIN_ROLE` to the specified `_admin` address.
     * - Initializes the vault control storage with the specified limits, pause states, and whitelist configuration.
     * - This function is intended to be called during the initialization phase and is protected by the `onlyInitializing` modifier.
     */
    function __initializeVaultControl(
        address _admin,
        uint256 _limit,
        bool _depositPause,
        bool _withdrawalPause,
        bool _depositWhitelist
    ) internal onlyInitializing {
        __ReentrancyGuard_init();
        __AccessControlEnumerable_init();
        _grantRole(DEFAULT_ADMIN_ROLE, _admin);

        __initializeVaultControlStorage(_limit, _depositPause, _withdrawalPause, _depositWhitelist);
    }

    /// @inheritdoc IVaultControl
    function setLimit(uint256 _limit) external onlyRole(SET_LIMIT_ROLE) {
        _setLimit(_limit);
    }

    /// @inheritdoc IVaultControl
    function pauseWithdrawals() external onlyRole(PAUSE_WITHDRAWALS_ROLE) {
        _setWithdrawalPause(true);
        _revokeRole(PAUSE_WITHDRAWALS_ROLE, _msgSender());
    }

    /// @inheritdoc IVaultControl
    function unpauseWithdrawals() external onlyRole(UNPAUSE_WITHDRAWALS_ROLE) {
        _setWithdrawalPause(false);
    }

    /// @inheritdoc IVaultControl
    function pauseDeposits() external onlyRole(PAUSE_DEPOSITS_ROLE) {
        _setDepositPause(true);
        _revokeRole(PAUSE_DEPOSITS_ROLE, _msgSender());
    }

    /// @inheritdoc IVaultControl
    function unpauseDeposits() external onlyRole(UNPAUSE_DEPOSITS_ROLE) {
        _setDepositPause(false);
    }

    /// @inheritdoc IVaultControl
    function setDepositWhitelist(bool status) external onlyRole(SET_DEPOSIT_WHITELIST_ROLE) {
        _setDepositWhitelist(status);
    }

    /// @inheritdoc IVaultControl
    function setDepositorWhitelistStatus(address account, bool status)
        external
        onlyRole(SET_DEPOSITOR_WHITELIST_STATUS_ROLE)
    {
        _setDepositorWhitelistStatus(account, status);
    }
}
IVaultControlStorage.sol 107 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";

/**
 * @title IVaultControlStorage
 * @notice Interface for interacting with the storage and control states of a vault.
 * @dev Provides functions to manage deposit and withdrawal controls, limits, and whitelisting of depositors.
 */
interface IVaultControlStorage {
    /**
     * @notice Storage structure for vault control data.
     * @dev Used to manage vault settings such as deposit and withdrawal states, limits, and whitelist functionality.
     * @param depositPause Indicates if deposits are currently paused.
     * @param withdrawalPause Indicates if withdrawals are currently paused.
     * @param limit The current limit on deposits.
     * @param depositWhitelist Indicates if the deposit whitelist is enabled.
     * @param isDepositorWhitelisted Mapping to track the whitelist status of each depositor by address.
     */
    struct Storage {
        bool depositPause;
        bool withdrawalPause;
        uint256 limit;
        bool depositWhitelist;
        mapping(address => bool) isDepositorWhitelisted;
    }

    /**
     * @notice Returns the current value of the `depositPause` state.
     * @dev When `true`, deposits into the vault are paused.
     * @return paused The current state of the deposit pause.
     */
    function depositPause() external view returns (bool);

    /**
     * @notice Returns the current value of the `withdrawalPause` state.
     * @dev When `true`, withdrawals from the vault are paused.
     * @return paused The current state of the withdrawal pause.
     */
    function withdrawalPause() external view returns (bool);

    /**
     * @notice Returns the current deposit limit.
     * @dev This limit can be applied to control the maximum allowed deposits.
     * @return limit The current limit value.
     */
    function limit() external view returns (uint256);

    /**
     * @notice Returns the current value of the `depositWhitelist` state.
     * @dev When `true`, only whitelisted addresses are allowed to deposit into the vault.
     * @return whitelistEnabled The current state of the deposit whitelist.
     */
    function depositWhitelist() external view returns (bool);

    /**
     * @notice Checks whether a given account is whitelisted for deposits.
     * @param account The address of the account to check.
     * @return isWhitelisted `true` if the account is whitelisted, `false` otherwise.
     */
    function isDepositorWhitelisted(address account) external view returns (bool);

    /**
     * @notice Emitted when the vault's deposit limit is updated.
     * @param limit The new limit value.
     * @param timestamp The time at which the limit was set.
     * @param sender The address of the account that set the new limit.
     */
    event LimitSet(uint256 limit, uint256 timestamp, address sender);

    /**
     * @notice Emitted when the deposit pause state is updated.
     * @param paused The new state of the deposit pause (`true` for paused, `false` for unpaused).
     * @param timestamp The time at which the pause state was set.
     * @param sender The address of the account that set the new state.
     */
    event DepositPauseSet(bool paused, uint256 timestamp, address sender);

    /**
     * @notice Emitted when the withdrawal pause state is updated.
     * @param paused The new state of the withdrawal pause (`true` for paused, `false` for unpaused).
     * @param timestamp The time at which the pause state was set.
     * @param sender The address of the account that set the new state.
     */
    event WithdrawalPauseSet(bool paused, uint256 timestamp, address sender);

    /**
     * @notice Emitted when the deposit whitelist state is updated.
     * @param status The new state of the deposit whitelist (`true` for enabled, `false` for disabled).
     * @param timestamp The time at which the whitelist state was set.
     * @param sender The address of the account that set the new state.
     */
    event DepositWhitelistSet(bool status, uint256 timestamp, address sender);

    /**
     * @notice Emitted when a depositor's whitelist status is updated.
     * @param account The address of the account whose whitelist status was updated.
     * @param status The new whitelist status (`true` for whitelisted, `false` for not whitelisted).
     * @param timestamp The time at which the whitelist status was set.
     * @param sender The address of the account that set the new status.
     */
    event DepositorWhitelistStatusSet(
        address account, bool status, uint256 timestamp, address sender
    );
}
IProtocolAdapter.sol 31 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

interface IProtocolAdapter {
    function vault() external view returns (address);

    function maxDeposit(address subvault) external view returns (uint256);

    function stakedAt(address subvault) external view returns (uint256);

    function assetOf(address subvault) external view returns (address);

    function validateRewardData(bytes calldata data) external view;

    function pushRewards(address rewardToken, bytes calldata farmData, bytes memory rewardData)
        external;

    function withdraw(
        address subvault,
        address withdrawalQueue,
        address receiver,
        uint256 request,
        address owner
    ) external;

    function deposit(address subvault, uint256 assets) external;

    function handleVault(address subvault) external returns (address withdrawalQueue);

    function areWithdrawalsPaused(address subvault, address account) external view returns (bool);
}
IWithdrawalQueue.sol 60 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

/**
 * @title IWithdrawalQueue
 * @notice Interface to handle the withdrawal process from the underlying vault.
 * @dev Provides functions to manage withdrawals, claimable assets, and interactions with vault epochs.
 */
interface IWithdrawalQueue {
    /**
     * @notice Returns the pending collateral amount for a specific account.
     * @param account The address of the account.
     * @return pendingAssets The total amount of pending collateral for the account.
     */
    function pendingAssetsOf(address account) external view returns (uint256);

    /**
     * @notice Returns the claimable collateral amount for a specific account.
     * @param account The address of the account.
     * @return claimableAssets The total amount of claimable collateral for the account.
     */
    function claimableAssetsOf(address account) external view returns (uint256);

    /**
     * @notice Claims collateral from the External Vault for a specified epoch.
     * @param epoch The epoch number from which to claim collateral.
     *
     * @custom:requirements
     * - The epoch MUST be claimable.
     * - There MUST be claimable withdrawals for the given epoch.
     *
     * @custom:effects
     * - Emits an `EpochClaimed` event.
     */
    function pull(uint256 epoch) external;

    /**
     * @notice Finalizes the collateral claim process for a specific account and transfers it to the recipient.
     * @dev Transfers the lesser of the claimable amount or the specified maximum amount to the recipient.
     * @param account The address of the account to claim collateral from.
     * @param recipient The address of the recipient receiving the collateral.
     * @param maxAmount The maximum amount of collateral to be claimed.
     * @return amount The actual amount of collateral claimed and transferred.
     *
     * @custom:requirements
     * - `msg.sender` MUST be the vault or the account itself.
     * - The claimable amount MUST be greater than zero.
     * - There MUST be claimable withdrawals for the given account.
     *
     * @custom:effects
     * - Emits a `Claimed` event.
     */
    function claim(address account, address recipient, uint256 maxAmount)
        external
        returns (uint256 amount);

    function transferPendingAssets(address recipient, uint256 assets) external;

    function claimer() external view returns (address);
}
IDepositStrategy.sol 14 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

interface IDepositStrategy {
    struct DepositData {
        uint256 subvaultIndex;
        uint256 deposit;
    }

    function calculateDepositAmounts(address vault, uint256 assets)
        external
        view
        returns (DepositData[] memory subvaultsData);
}
IRebalanceStrategy.sol 16 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

interface IRebalanceStrategy {
    struct RebalanceData {
        uint256 subvaultIndex;
        uint256 deposit;
        uint256 claimable;
        uint256 staked;
    }

    function calculateRebalanceAmounts(address vault)
        external
        view
        returns (RebalanceData[] memory subvaultsData);
}
IWithdrawalStrategy.sol 16 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

interface IWithdrawalStrategy {
    struct WithdrawalData {
        uint256 subvaultIndex;
        uint256 claimable;
        uint256 pending;
        uint256 staked;
    }

    function calculateWithdrawalAmounts(address vault, uint256 amount)
        external
        view
        returns (WithdrawalData[] memory subvaultsData);
}
IDefaultCollateral.sol 185 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IDefaultCollateral is IERC20 {
    /**
     * @notice Emitted when debt is issued.
     * @param issuer address of the debt's issuer
     * @param recipient address that should receive the underlying asset
     * @param debtIssued amount of the debt issued
     */
    event IssueDebt(address indexed issuer, address indexed recipient, uint256 debtIssued);

    /**
     * @notice Emitted when debt is repaid.
     * @param issuer address of the debt's issuer
     * @param recipient address that received the underlying asset
     * @param debtRepaid amount of the debt repaid
     */
    event RepayDebt(address indexed issuer, address indexed recipient, uint256 debtRepaid);

    /**
     * @notice Get the collateral's underlying asset.
     * @return asset address of the underlying asset
     */
    function asset() external view returns (address);

    /**
     * @notice Get a total amount of repaid debt.
     * @return total repaid debt
     */
    function totalRepaidDebt() external view returns (uint256);

    /**
     * @notice Get an amount of repaid debt created by a particular issuer.
     * @param issuer address of the debt's issuer
     * @return particular issuer's repaid debt
     */
    function issuerRepaidDebt(address issuer) external view returns (uint256);

    /**
     * @notice Get an amount of repaid debt to a particular recipient.
     * @param recipient address that received the underlying asset
     * @return particular recipient's repaid debt
     */
    function recipientRepaidDebt(address recipient) external view returns (uint256);

    /**
     * @notice Get an amount of repaid debt for a particular issuer-recipient pair.
     * @param issuer address of the debt's issuer
     * @param recipient address that received the underlying asset
     * @return particular pair's repaid debt
     */
    function repaidDebt(address issuer, address recipient) external view returns (uint256);

    /**
     * @notice Get a total amount of debt.
     * @return total debt
     */
    function totalDebt() external view returns (uint256);

    /**
     * @notice Get a current debt created by a particular issuer.
     * @param issuer address of the debt's issuer
     * @return particular issuer's debt
     */
    function issuerDebt(address issuer) external view returns (uint256);

    /**
     * @notice Get a current debt to a particular recipient.
     * @param recipient address that should receive the underlying asset
     * @return particular recipient's debt
     */
    function recipientDebt(address recipient) external view returns (uint256);

    /**
     * @notice Get a current debt for a particular issuer-recipient pair.
     * @param issuer address of the debt's issuer
     * @param recipient address that should receive the underlying asset
     * @return particular pair's debt
     */
    function debt(address issuer, address recipient) external view returns (uint256);

    /**
     * @notice Burn a given amount of the collateral, and increase a debt of the underlying asset for the caller.
     * @param recipient address that should receive the underlying asset
     * @param amount amount of the collateral
     */
    function issueDebt(address recipient, uint256 amount) external;

    error NotLimitIncreaser();
    error InsufficientDeposit();
    error ExceedsLimit();
    error InsufficientWithdraw();
    error InsufficientIssueDebt();

    /**
     * @notice Emmited when deposit happens.
     * @param depositor address of the depositor
     * @param recipient address of the collateral's recipient
     * @param amount amount of the collateral minted
     */
    event Deposit(address indexed depositor, address indexed recipient, uint256 amount);

    /**
     * @notice Emmited when withdrawal happens.
     * @param withdrawer address of the withdrawer
     * @param recipient address of the underlying asset's recipient
     * @param amount amount of the collateral burned
     */
    event Withdraw(address indexed withdrawer, address indexed recipient, uint256 amount);

    /**
     * @notice Emmited when limit is increased.
     * @param amount amount to increase the limit by
     */
    event IncreaseLimit(uint256 amount);

    /**
     * @notice Emmited when new limit increaser is set.
     * @param limitIncreaser address of the new limit increaser
     */
    event SetLimitIncreaser(address indexed limitIncreaser);

    /**
     * @notice Get a maximum possible collateral total supply.
     * @return maximum collateral total supply
     */
    function limit() external view returns (uint256);

    /**
     * @notice Get an address of the limit increaser.
     * @return address of the limit increaser
     */
    function limitIncreaser() external view returns (address);

    /**
     * @notice Deposit a given amount of the underlying asset, and mint the collateral to a particular recipient.
     * @param recipient address of the collateral's recipient
     * @param amount amount of the underlying asset
     * @return amount of the collateral minted
     */
    function deposit(address recipient, uint256 amount) external returns (uint256);

    /**
     * @notice Deposit a given amount of the underlying asset using a permit functionality, and mint the collateral to a particular recipient.
     * @param recipient address of the collateral's recipient
     * @param amount amount of the underlying asset
     * @param deadline timestamp of the signature's deadline
     * @param v v component of the signature
     * @param r r component of the signature
     * @param s s component of the signature
     * @return amount of the collateral minted
     */
    function deposit(
        address recipient,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256);

    /**
     * @notice Withdraw a given amount of the underlying asset, and transfer it to a particular recipient.
     * @param recipient address of the underlying asset's recipient
     * @param amount amount of the underlying asset
     */
    function withdraw(address recipient, uint256 amount) external;

    /**
     * @notice Increase a limit of maximum collateral total supply.
     * @param amount amount to increase the limit by
     * @dev Called only by limitIncreaser.
     */
    function increaseLimit(uint256 amount) external;

    /**
     * @notice Set a new limit increaser.
     * @param limitIncreaser address of the new limit increaser
     * @dev Called only by limitIncreaser.
     */
    function setLimitIncreaser(address limitIncreaser) external;
}
Initializable.sol 228 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

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

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

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

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

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

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

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

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

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

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

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

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

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
ERC4626Upgradeable.sol 311 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ERC20Upgradeable} from "../ERC20Upgradeable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * [CAUTION]
 * ====
 * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
 * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
 * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
 * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
 * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
 * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
 * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
 * expensive than it is profitable. More details about the underlying math can be found
 * xref:erc4626.adoc#inflation-attack[here].
 *
 * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
 * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
 * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
 * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
 * `_convertToShares` and `_convertToAssets` functions.
 *
 * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
 * ====
 */
abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626 {
    using Math for uint256;

    /// @custom:storage-location erc7201:openzeppelin.storage.ERC4626
    struct ERC4626Storage {
        IERC20 _asset;
        uint8 _underlyingDecimals;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC4626")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC4626StorageLocation = 0x0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00;

    function _getERC4626Storage() private pure returns (ERC4626Storage storage $) {
        assembly {
            $.slot := ERC4626StorageLocation
        }
    }

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
     */
    function __ERC4626_init(IERC20 asset_) internal onlyInitializing {
        __ERC4626_init_unchained(asset_);
    }

    function __ERC4626_init_unchained(IERC20 asset_) internal onlyInitializing {
        ERC4626Storage storage $ = _getERC4626Storage();
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        $._underlyingDecimals = success ? assetDecimals : 18;
        $._asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeCall(IERC20Metadata.decimals, ())
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /**
     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
     * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
     *
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20Metadata, ERC20Upgradeable) returns (uint8) {
        ERC4626Storage storage $ = _getERC4626Storage();
        return $._underlyingDecimals + _decimalsOffset();
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual returns (address) {
        ERC4626Storage storage $ = _getERC4626Storage();
        return address($._asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual returns (uint256) {
        ERC4626Storage storage $ = _getERC4626Storage();
        return $._asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        uint256 maxAssets = maxDeposit(receiver);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);
        }

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}.
     *
     * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
     * In this case, the shares will be minted without requiring any assets to be deposited.
     */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxAssets = maxWithdraw(owner);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
        }

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxShares = maxRedeem(owner);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
        }

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
        ERC4626Storage storage $ = _getERC4626Storage();
        // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom($._asset, caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        ERC4626Storage storage $ = _getERC4626Storage();
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer($._asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _decimalsOffset() internal view virtual returns (uint8) {
        return 0;
    }
}
SafeERC20.sol 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

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

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

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

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

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

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

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

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

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

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

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
EnumerableSet.sol 378 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
IVaultControl.sol 84 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "./IVaultControlStorage.sol";
import {AccessControlEnumerableUpgradeable} from
    "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";
import {ERC4626Upgradeable} from
    "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {ReentrancyGuardUpgradeable} from
    "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title IVaultControl
 * @notice Interface for controlling the operational state of a vault, including deposits, withdrawals, limits, and whitelisting.
 * @dev Extends IVaultControlStorage for managing storage and settings related to vault operations.
 */
interface IVaultControl is IVaultControlStorage {
    /**
     * @notice Sets a new limit for the vault to restrict the total value of assets held.
     * @dev Can only be called by an address with the `SET_LIMIT_ROLE`.
     * @param _limit The new limit value to be set.
     */
    function setLimit(uint256 _limit) external;

    /**
     * @notice Pauses withdrawals from the vault.
     * @dev Once paused, no withdrawals can be processed until unpaused.
     * @dev Can only be called by an address with the `PAUSE_WITHDRAWALS_ROLE`.
     * @custom:effects
     * - Emits a `WithdrawalPauseSet` event with `paused` set to `true`.
     * - Revokes the `PAUSE_WITHDRAWALS_ROLE` from `msg.sender`
     */
    function pauseWithdrawals() external;

    /**
     * @notice Unpauses withdrawals from the vault.
     * @dev Once unpaused, withdrawals can be processed again.
     * @dev Can only be called by an address with the `UNPAUSE_WITHDRAWALS_ROLE`.
     * @custom:effects
     * - Emits a `WithdrawalPauseSet` event with `paused` set to `false`.
     */
    function unpauseWithdrawals() external;

    /**
     * @notice Pauses deposits into the vault.
     * @dev Once paused, no deposits can be made until unpaused.
     * @dev Can only be called by an address with the `PAUSE_DEPOSITS_ROLE`.
     * @custom:effects
     * - Emits a `DepositPauseSet` event with `paused` set to `true`.
     * - Revokes the `PAUSE_DEPOSITS_ROLE` from `msg.sender`
     */
    function pauseDeposits() external;

    /**
     * @notice Unpauses deposits into the vault.
     * @dev Once unpaused, deposits can be made again.
     * @dev Can only be called by an address with the `UNPAUSE_DEPOSITS_ROLE`.
     * @custom:effects
     * - Emits a `DepositPauseSet` event with `paused` set to `false`.
     */
    function unpauseDeposits() external;

    /**
     * @notice Sets the deposit whitelist status for the vault.
     * @dev When the whitelist is enabled, only addresses on the whitelist can deposit into the vault.
     * @dev Can only be called by an address with the `SET_DEPOSIT_WHITELIST_ROLE`.
     * @param status The new whitelist status (`true` to enable, `false` to disable).
     * @custom:effects
     * - Emits a `DepositWhitelistSet` event indicating the new whitelist status.
     */
    function setDepositWhitelist(bool status) external;

    /**
     * @notice Updates the whitelist status of a specific account.
     * @dev Allows the contract to grant or revoke the ability of an account to make deposits based on the whitelist.
     * @dev Can only be called by an address with the `SET_DEPOSITOR_WHITELIST_STATUS_ROLE`.
     * @param account The address of the account to be updated.
     * @param status The new whitelist status for the account (`true` for whitelisted, `false` for not whitelisted).
     * @custom:effects
     * - Emits a `DepositorWhitelistStatusSet` event indicating the updated whitelist status for the account.
     */
    function setDepositorWhitelistStatus(address account, bool status) external;
}
ContextUpgradeable.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
ERC20Upgradeable.sol 341 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
    /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
    struct ERC20Storage {
        mapping(address account => uint256) _balances;

        mapping(address account => mapping(address spender => uint256)) _allowances;

        uint256 _totalSupply;

        string _name;
        string _symbol;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;

    function _getERC20Storage() private pure returns (ERC20Storage storage $) {
        assembly {
            $.slot := ERC20StorageLocation
        }
    }

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        ERC20Storage storage $ = _getERC20Storage();
        $._name = name_;
        $._symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        ERC20Storage storage $ = _getERC20Storage();
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            $._totalSupply += value;
        } else {
            uint256 fromBalance = $._balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                $._balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                $._totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                $._balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        ERC20Storage storage $ = _getERC20Storage();
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        $._allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
IERC4626.sol 230 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
AccessControlEnumerableUpgradeable.sol 92 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol";
import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerable, AccessControlUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControlEnumerable
    struct AccessControlEnumerableStorage {
        mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlEnumerable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlEnumerableStorageLocation = 0xc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000;

    function _getAccessControlEnumerableStorage() private pure returns (AccessControlEnumerableStorage storage $) {
        assembly {
            $.slot := AccessControlEnumerableStorageLocation
        }
    }

    function __AccessControlEnumerable_init() internal onlyInitializing {
    }

    function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].length();
    }

    /**
     * @dev Overload {AccessControl-_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        bool granted = super._grantRole(role, account);
        if (granted) {
            $._roleMembers[role].add(account);
        }
        return granted;
    }

    /**
     * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        bool revoked = super._revokeRole(role, account);
        if (revoked) {
            $._roleMembers[role].remove(account);
        }
        return revoked;
    }
}
ReentrancyGuardUpgradeable.sol 105 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

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

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

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

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

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

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        $._status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if ($._status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

    function _nonReentrantAfter() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        $._status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        return $._status == ENTERED;
    }
}
draft-IERC6093.sol 161 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
IAccessControlEnumerable.sol 31 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlEnumerable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
AccessControlUpgradeable.sol 233 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
ERC165Upgradeable.sol 33 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165Upgradeable is Initializable, IERC165 {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Read Contract

ADD_SUBVAULT_ROLE 0x3a2bbe49 → bytes32
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
REBALANCE_ROLE 0x5680e145 → bytes32
REMOVE_SUBVAULT_ROLE 0x2b181e38 → bytes32
SET_ADAPTER_ROLE 0xfa3468b8 → bytes32
SET_DEFAULT_COLLATERAL_ROLE 0x0dce8456 → bytes32
SET_FARM_ROLE 0xc2e9dcd6 → bytes32
SET_STRATEGY_ROLE 0xbaaf36b5 → bytes32
adapterOf 0x13462e72 → address
allowance 0xdd62ed3e → uint256
asset 0x38d52e0f → address
balanceOf 0x70a08231 → uint256
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
decimals 0x313ce567 → uint8
defaultCollateral 0xaeb0f164 → address
depositPause 0x6e22558d → bool
depositStrategy 0x870ba746 → address
depositWhitelist 0x48d3b775 → bool
eigenLayerAdapter 0x72920c90 → address
erc4626Adapter 0x32ca0997 → address
farmCount 0x0a8e214c → uint256
farmIdAt 0x4dc3e37b → uint256
farmIds 0xabc5f1bc → uint256[]
farmIdsContains 0xd6276b26 → bool
getRoleAdmin 0x248a9ca3 → bytes32
getRoleMember 0x9010d07c → address
getRoleMemberCount 0xca15c873 → uint256
hasRole 0x91d14854 → bool
indexOfSubvault 0x8ebd0098 → uint256
isDepositorWhitelisted 0x794b15b7 → bool
limit 0xa4d66daf → uint256
maxDeposit 0x402d267d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxWithdraw 0xce96cb77 → uint256
name 0x06fdde03 → string
previewDeposit 0xef8b30f7 → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewWithdraw 0x0a28a477 → uint256
rebalanceStrategy 0xdb7a3ea3 → address
rewardData 0x3e3cc239 → tuple
subvaultAt 0x9bd0911b → tuple
subvaultsCount 0xb42b544f → uint256
supportsInterface 0x01ffc9a7 → bool
symbioticAdapter 0x65e82ce3 → address
symbol 0x95d89b41 → string
totalAssets 0x01e1d114 → uint256
totalSupply 0x18160ddd → uint256
withdrawalPause 0xc19a2aa7 → bool
withdrawalStrategy 0x1dcae164 → address

Write Contract 31 functions

These functions modify contract state and require a wallet transaction to execute.

addSubvault 0x4b6a58fe
address vault
uint8 protocol
approve 0x095ea7b3
address spender
uint256 value
returns: bool
deposit 0x2e2d2984
uint256 assets
address receiver
address referral
returns: uint256
deposit 0x6e553f65
uint256 assets
address receiver
returns: uint256
grantRole 0x2f2ff15d
bytes32 role
address account
initialize 0x588570a5
tuple initParams
mint 0x94bf804d
uint256 shares
address receiver
returns: uint256
pauseDeposits 0x02191980
No parameters
pauseWithdrawals 0x56bb54a7
No parameters
pushRewards 0x922bafd0
uint256 farmId
bytes farmData
rebalance 0x7d7c2a1c
No parameters
redeem 0xba087652
uint256 shares
address receiver
address owner
returns: uint256
removeSubvault 0xec94c452
address subvault
renounceRole 0x36568abe
bytes32 role
address callerConfirmation
revokeRole 0xd547741f
bytes32 role
address account
setDefaultCollateral 0x36f60619
address defaultCollateral_
setDepositStrategy 0x27cfa7ef
address newDepositStrategy
setDepositWhitelist 0x4105a7dd
bool status
setDepositorWhitelistStatus 0xa2861466
address account
bool status
setERC4626Adapter 0xf24d6550
address adapter_
setEigenLayerAdapter 0x27daaaac
address adapter_
setLimit 0x27ea6f2b
uint256 _limit
setRebalanceStrategy 0xf4291968
address newRebalanceStrategy
setRewardsData 0x0dcab78c
uint256 farmId
tuple rewardData
setSymbioticAdapter 0xc012cf77
address adapter_
setWithdrawalStrategy 0x4f62421f
address newWithdrawalStrategy
transfer 0xa9059cbb
address to
uint256 value
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 value
returns: bool
unpauseDeposits 0x63d8882a
No parameters
unpauseWithdrawals 0xe4c4be58
No parameters
withdraw 0xb460af94
uint256 assets
address receiver
address owner
returns: uint256

Recent Transactions

No transactions found for this address