Address Contract Verified
Address
0x0C5BC4C8406Fe03214D18bbf2962Ae2fa378c6f7
Balance
0 ETH
Nonce
1
Code Size
24481 bytes
Creator
0x188858AC...afA1 at tx 0x05ecdb5c...953266
Indexed Transactions
0
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