Address Contract Partially Verified
Address
0x0159AEA190C7bEa09873B9b42Fe8fD836DB8a254
Balance
0 ETH
Nonce
71
Code Size
21339 bytes
Creator
0x188858AC...afA1 at tx 0xc9e40153...6ceb0c
Indexed Transactions
0
Contract Bytecode
21339 bytes
0x608060405234801561000f575f80fd5b506004361061007a575f3560e01c8063374040171161005857806337404017146101105780634238b3af146101305780635dbe8f8b14610152578063a8c62e7614610173575f80fd5b806308e85a781461007e578063187a4ad9146100c257806323ee74fb146100e9575b5f80fd5b6100a57f000000000000000000000000ab253b304b0bfbe38ef7ea1f086d01a6ce1c502881565b6040516001600160a01b0390911681526020015b60405180910390f35b6100a57f0000000000000000000000000c5bc4c8406fe03214d18bbf2962ae2fa378c6f781565b6100a57f000000000000000000000000aeb6bdd95c502390db8f52c8909f703e9af6a34681565b61012361011e366004611684565b61019a565b6040516100b99190611826565b61014361013e366004611868565b6103c4565b6040516100b9939291906118a6565b610165610160366004611868565b61159e565b6040519081526020016100b9565b6100a57f0000000000000000000000003aa61e6196fb3eb1170e578ad924898624f54ad681565b6101a26115cd565b5f828152602081815260409182902082516060808201855282546001600160a01b039081168352600184015481168386015285516101e081018752600285018054831682526003860154831696820196909652600485015482168188015260058501548216928101929092526006840154811660808301526007840154811660a083015260088401541660c0820152600983015460e0820152600a83015460ff808216151561010084810191909152909104161515610120820152600b83018054929593949386019391926101408401919061027d906118da565b80601f01602080910402602001604051908101604052809291908181526020018280546102a9906118da565b80156102f45780601f106102cb576101008083540402835291602001916102f4565b820191905f5260205f20905b8154815290600101906020018083116102d757829003601f168201915b50505050508152602001600a8201805461030d906118da565b80601f0160208091040260200160405190810160405280929190818152602001828054610339906118da565b80156103845780601f1061035b57610100808354040283529160200191610384565b820191905f5260205f20905b81548152906001019060200180831161036757829003601f168201915b5050509183525050600b8201546001600160401b038082166020840152600160401b909104166040820152600c9091015460609091015290525092915050565b5f806103ce6115f0565b5f6103d88561159e565b9050807f0000000000000000000000000c5bc4c8406fe03214d18bbf2962ae2fa378c6f761040c604088016020890161192d565b604080515f81526020810191829052906104259061166a565b6104319392919061195a565b8190604051809103905ff590508015801561044e573d5f803e3d5ffd5b50935080847f000000000000000000000000aeb6bdd95c502390db8f52c8909f703e9af6a3467f000000000000000000000000ab253b304b0bfbe38ef7ea1f086d01a6ce1c50286104a560408a0160208b0161192d565b6040516104b190611677565b6001600160a01b0394851681529284166020840152908316604083015290911660608201526080018190604051809103905ff59050801580156104f6573d5f803e3d5ffd5b50604080516101e08101825230815260e088013560208201529194505f9190810161052961012089016101008a01611994565b1515815260200161054261014089016101208a01611994565b151581526020015f61055a60a08a0160808b0161192d565b6001600160a01b03161415815260200161057a60c0890160a08a0161192d565b6001600160a01b031681526020016105966101408901896119ad565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050908252506020016105dd6101608901896119ad565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050908252506001600160a01b037f0000000000000000000000003aa61e6196fb3eb1170e578ad924898624f54ad6166020820181905260408201819052606082015260800161066360e0890160c08a0161192d565b6001600160a01b03908116825286811660208301525f60408084018290526060909301529051639bfe9e2360e01b8152919250861690639bfe9e23906106ad9084906004016119f6565b5f604051808303815f87803b1580156106c4575f80fd5b505af11580156106d6573d5f803e3d5ffd5b505f92506106ed91505060a088016080890161192d565b6001600160a01b03161461085057604051632f2ff15d60e01b81527f1867ae69910bc7238ccbbb445aa11a0dbdd472b851b7fac0c991101aca0a360d60048201819052306024830152906001600160a01b03871690632f2ff15d906044015f604051808303815f87803b158015610762575f80fd5b505af1158015610774573d5f803e3d5ffd5b5050506001600160a01b038716905063a286146661079860a08a0160808b0161192d565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152600160248201526044015f604051808303815f87803b1580156107dd575f80fd5b505af11580156107ef573d5f803e3d5ffd5b5050604051631b2b455f60e11b8152600481018490523060248201526001600160a01b03891692506336568abe91506044015f604051808303815f87803b158015610838575f80fd5b505af115801561084a573d5f803e3d5ffd5b50505050505b846001600160a01b0316632f2ff15d866001600160a01b031663a217fddf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561089b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108bf9190611b60565b6108cc60208a018a61192d565b6040516001600160e01b031960e085901b16815260048101929092526001600160a01b031660248201526044015f604051808303815f87803b158015610910575f80fd5b505af1158015610922573d5f803e3d5ffd5b50505050846001600160a01b0316632f2ff15d866001600160a01b031663c2e9dcd66040518163ffffffff1660e01b8152600401602060405180830381865afa158015610971573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109959190611b60565b6109a260208a018a61192d565b6040516001600160e01b031960e085901b16815260048101929092526001600160a01b031660248201526044015f604051808303815f87803b1580156109e6575f80fd5b505af11580156109f8573d5f803e3d5ffd5b50505050846001600160a01b0316632f2ff15d866001600160a01b0316635680e1456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a47573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a6b9190611b60565b610a7b60608a0160408b0161192d565b6040516001600160e01b031960e085901b16815260048101929092526001600160a01b031660248201526044015f604051808303815f87803b158015610abf575f80fd5b505af1158015610ad1573d5f803e3d5ffd5b50505050846001600160a01b0316632f2ff15d866001600160a01b0316633a2bbe496040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b449190611b60565b610b5460608a0160408b0161192d565b6040516001600160e01b031960e085901b16815260048101929092526001600160a01b031660248201526044015f604051808303815f87803b158015610b98575f80fd5b505af1158015610baa573d5f803e3d5ffd5b5050506001600160a01b0386169050632f2ff15d7f6e5811d60b7d57973a97208b6158fed3b8e064ca747403e6a8c81f56a8f9e75f610bef60608a0160408b0161192d565b6040516001600160e01b031960e085901b16815260048101929092526001600160a01b031660248201526044015f604051808303815f87803b158015610c33575f80fd5b505af1158015610c45573d5f803e3d5ffd5b50505050846001600160a01b0316632f2ff15d7f0000000000000000000000003aa61e6196fb3eb1170e578ad924898624f54ad66001600160a01b031663cf8dda3b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cb4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cd89190611b60565b610ce860608a0160408b0161192d565b6040516001600160e01b031960e085901b16815260048101929092526001600160a01b031660248201526044015f604051808303815f87803b158015610d2c575f80fd5b505af1158015610d3e573d5f803e3d5ffd5b505f9250610d55915050608088016060890161192d565b6001600160a01b0316146112a857846001600160a01b0316632f2ff15d7f0000000000000000000000003aa61e6196fb3eb1170e578ad924898624f54ad66001600160a01b031663cf8dda3b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dce573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610df29190611b60565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044015f604051808303815f87803b158015610e2e575f80fd5b505af1158015610e40573d5f803e3d5ffd5b50505050846001600160a01b0316632f2ff15d866001600160a01b0316633a2bbe496040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eb39190611b60565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044015f604051808303815f87803b158015610eef575f80fd5b505af1158015610f01573d5f803e3d5ffd5b5050506001600160a01b0386169050634b6a58fe610f256080890160608a0161192d565b5f6040518363ffffffff1660e01b8152600401610f43929190611b77565b5f604051808303815f87803b158015610f5a575f80fd5b505af1158015610f6c573d5f803e3d5ffd5b505f925060019150610f7b9050565b604051908082528060200260200182016040528015610fa4578160200160208202803683370190505b509050610fb7608088016060890161192d565b815f81518110610fc957610fc9611bb0565b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092525f91816020015b604080518082019091525f8082526020820152815260200190600190039081610ff8575050604080518082019091529091508061103d6101a08b016101808c01611bda565b6001600160401b0316815260200161105d6101c08b016101a08c01611bda565b6001600160401b0316815250815f8151811061107b5761107b611bb0565b6020908102919091010152604051637166956d60e01b81526001600160a01b037f0000000000000000000000003aa61e6196fb3eb1170e578ad924898624f54ad61690637166956d906110d6908a9086908690600401611bf3565b5f604051808303815f87803b1580156110ed575f80fd5b505af11580156110ff573d5f803e3d5ffd5b50505050866001600160a01b03166336568abe7f0000000000000000000000003aa61e6196fb3eb1170e578ad924898624f54ad66001600160a01b031663cf8dda3b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561116e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111929190611b60565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044015f604051808303815f87803b1580156111ce575f80fd5b505af11580156111e0573d5f803e3d5ffd5b50505050866001600160a01b03166336568abe886001600160a01b0316633a2bbe496040518163ffffffff1660e01b8152600401602060405180830381865afa15801561122f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112539190611b60565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044015f604051808303815f87803b15801561128f575f80fd5b505af11580156112a1573d5f803e3d5ffd5b5050505050505b846001600160a01b03166336568abe866001600160a01b031663a217fddf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113179190611b60565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044015f604051808303815f87803b158015611353575f80fd5b505af1158015611365573d5f803e3d5ffd5b5050505081846001600160a01b0316866001600160a01b03167f941c9195b6fb499b0f8e1b227134859845dadb7b8e5f025a9e682118ea8bb34a896040516113ad9190611d0c565b60405180910390a46040518060600160405280866001600160a01b03168152602001856001600160a01b03168152602001876113e890611f83565b90525f8381526020818152604091829020835181546001600160a01b03199081166001600160a01b039283161783558584015160018401805483169184169190911790558585015180516002850180548416918516919091178155948101516003850180548416918516919091179055948501516004840180548316918416919091179055606085015160058401805483169184169190911790556080850151600684018054831691841691909117905560a0850151600784018054831691841691909117905560c0850151600884018054909216921691909117905560e0830151600982015561010080840151600a8301805461012087015161ffff1990911692151561ff0019169290921791151590920217905561014083015190929190600b8401906115179082612112565b50610160820151600a82019061152d9082612112565b50610180820151600b820180546101a08501516001600160401b03908116600160401b026fffffffffffffffffffffffffffffffff199092169316929092179190911790556101c090910151600c9091015550859050848761158e81611f83565b9299919850919650945050505050565b5f816040516020016115b09190611d0c565b604051602081830303815290604052805190602001209050919050565b604080516060810182525f80825260208201529081016115eb6115f0565b905290565b604080516101e0810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820183905261010082018390526101208201839052610140820181905261016082015261018081018290526101a081018290526101c081019190915290565b610dbc806121d283390190565b61239880612f8e83390190565b5f60208284031215611694575f80fd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b80516001600160a01b031682525f6101e060208301516116f460208601826001600160a01b03169052565b50604083015161170f60408601826001600160a01b03169052565b50606083015161172a60608601826001600160a01b03169052565b50608083015161174560808601826001600160a01b03169052565b5060a083015161176060a08601826001600160a01b03169052565b5060c083015161177b60c08601826001600160a01b03169052565b5060e083015160e08501526101008084015161179a8287018215159052565b505061012083810151151590850152610140808401518186018390526117c28387018261169b565b9250505061016080840151858303828701526117de838261169b565b92505050610180808401516117fd828701826001600160401b03169052565b50506101a0838101516001600160401b0316908501526101c09283015192909301919091525090565b602081525f60018060a01b0380845116602084015280602085015116604084015250604083015160608084015261186060808401826116c9565b949350505050565b5f60208284031215611878575f80fd5b81356001600160401b0381111561188d575f80fd5b82016101e0818503121561189f575f80fd5b9392505050565b6001600160a01b038481168252831660208201526060604082018190525f906118d1908301846116c9565b95945050505050565b600181811c908216806118ee57607f821691505b60208210810361190c57634e487b7160e01b5f52602260045260245ffd5b50919050565b80356001600160a01b0381168114611928575f80fd5b919050565b5f6020828403121561193d575f80fd5b61189f82611912565b634e487b7160e01b5f52604160045260245ffd5b6001600160a01b038481168252831660208201526060604082018190525f906118d19083018461169b565b80358015158114611928575f80fd5b5f602082840312156119a4575f80fd5b61189f82611985565b5f808335601e198436030181126119c2575f80fd5b8301803591506001600160401b038211156119db575f80fd5b6020019150368190038213156119ef575f80fd5b9250929050565b60208152611a106020820183516001600160a01b03169052565b602082015160408201525f6040830151611a2e606084018215159052565b506060830151801515608084015250608083015180151560a08401525060a08301516001600160a01b03811660c08401525060c08301516101e08060e0850152611a7c61020085018361169b565b915060e0850151610100601f198685030181870152611a9b848361169b565b935080870151915050610120611abb818701836001600160a01b03169052565b8601519050610140611ad7868201836001600160a01b03169052565b8601519050610160611af3868201836001600160a01b03169052565b8601519050610180611b0f868201836001600160a01b03169052565b86015190506101a0611b2b868201836001600160a01b03169052565b86015190506101c0611b47868201836001600160a01b03169052565b909501516001600160a01b031693019290925250919050565b5f60208284031215611b70575f80fd5b5051919050565b6001600160a01b03831681526040810160038310611ba357634e487b7160e01b5f52602160045260245ffd5b8260208301529392505050565b634e487b7160e01b5f52603260045260245ffd5b80356001600160401b0381168114611928575f80fd5b5f60208284031215611bea575f80fd5b61189f82611bc4565b6001600160a01b0384811682526060602080840182905285519184018290525f92868201929091906080860190855b81811015611c40578551851683529483019491830191600101611c22565b50506040935085810360408701528092508651808252828201935082880191505f5b81811015611c9457825180516001600160401b0390811687529085015116848601529385019391830191600101611c62565b50929998505050505050505050565b5f808335601e19843603018112611cb8575f80fd5b83016020810192503590506001600160401b03811115611cd6575f80fd5b8036038213156119ef575f80fd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b60208152611d2d60208201611d2084611912565b6001600160a01b03169052565b5f611d3a60208401611912565b6001600160a01b038116604084015250611d5660408401611912565b6001600160a01b038116606084015250611d7260608401611912565b6001600160a01b038116608084015250611d8e60808401611912565b6001600160a01b03811660a084015250611daa60a08401611912565b6001600160a01b03811660c084015250611dc660c08401611912565b6001600160a01b03811660e08401525061010060e084013581840152611ded818501611985565b9050610120611dff8185018315159052565b611e0a818601611985565b915050610140611e1d8185018315159052565b611e2981860186611ca3565b925090506101e06101608181870152611e4761020087018585611ce4565b9350611e5581880188611ca3565b93509050610180601f198786030181880152611e72858584611ce4565b9450611e7f818901611bc4565b935050506101a0611e9a818701846001600160401b03169052565b611ea5818801611bc4565b9250506101c0611ebf818701846001600160401b03169052565b9590950135939094019290925250919050565b6040516101e081016001600160401b0381118282101715611ef557611ef5611946565b60405290565b5f82601f830112611f0a575f80fd5b81356001600160401b0380821115611f2457611f24611946565b604051601f8301601f19908116603f01168101908282118183101715611f4c57611f4c611946565b81604052838152866020858801011115611f64575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f6101e08236031215611f94575f80fd5b611f9c611ed2565b611fa583611912565b8152611fb360208401611912565b6020820152611fc460408401611912565b6040820152611fd560608401611912565b6060820152611fe660808401611912565b6080820152611ff760a08401611912565b60a082015261200860c08401611912565b60c082015260e083013560e0820152610100612025818501611985565b90820152610120612037848201611985565b90820152610140838101356001600160401b0380821115612056575f80fd5b61206236838801611efb565b8385015261016092508286013591508082111561207d575f80fd5b5061208a36828701611efb565b82840152505061018061209e818501611bc4565b908201526101a06120b0848201611bc4565b908201526101c092830135928101929092525090565b601f82111561210d57805f5260205f20601f840160051c810160208510156120eb5750805b601f840160051c820191505b8181101561210a575f81556001016120f7565b50505b505050565b81516001600160401b0381111561212b5761212b611946565b61213f8161213984546118da565b846120c6565b602080601f831160018114612172575f841561215b5750858301515b5f19600386901b1c1916600185901b1785556121c9565b5f85815260208120601f198616915b828110156121a057888601518255948401946001909101908401612181565b50858210156121bd57878501515f19600388901b60f8161c191681555b505060018460011b0185555b50505050505056fe60a0604052604051610dbc380380610dbc8339810160408190526100229161036a565b828161002e828261008c565b50508160405161003d9061032e565b6001600160a01b039091168152602001604051809103905ff080158015610066573d5f803e3d5ffd5b506001600160a01b031660805261008461007f60805190565b6100ea565b50505061044b565b61009582610157565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100de576100d982826101d5565b505050565b6100e6610248565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6101295f80516020610d9c833981519152546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a161015481610269565b50565b806001600160a01b03163b5f0361019157604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f80846001600160a01b0316846040516101f19190610435565b5f60405180830381855af49150503d805f8114610229576040519150601f19603f3d011682016040523d82523d5f602084013e61022e565b606091505b50909250905061023f8583836102a6565b95945050505050565b34156102675760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b03811661029257604051633173bdd160e11b81525f6004820152602401610188565b805f80516020610d9c8339815191526101b4565b6060826102bb576102b682610305565b6102fe565b81511580156102d257506001600160a01b0384163b155b156102fb57604051639996b31560e01b81526001600160a01b0385166004820152602401610188565b50805b9392505050565b8051156103155780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6104e7806108b583390190565b80516001600160a01b0381168114610351575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f805f6060848603121561037c575f80fd5b6103858461033b565b92506103936020850161033b565b60408501519092506001600160401b03808211156103af575f80fd5b818601915086601f8301126103c2575f80fd5b8151818111156103d4576103d4610356565b604051601f8201601f19908116603f011681019083821181831017156103fc576103fc610356565b81604052828152896020848701011115610414575f80fd5b8260208601602083015e5f6020848301015280955050505050509250925092565b5f82518060208501845e5f920191825250919050565b6080516104536104625f395f601001526104535ff3fe608060405261000c61000e565b005b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361007a575f356001600160e01b03191663278f794360e11b14610070576040516334ad5dbb60e21b815260040160405180910390fd5b610078610082565b565b6100786100b0565b5f806100913660048184610303565b81019061009e919061033e565b915091506100ac82826100c0565b5050565b6100786100bb61011a565b610151565b6100c98261016f565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101125761010d82826101ea565b505050565b6100ac61025c565b5f61014c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f80375f80365f845af43d5f803e80801561016b573d5ff35b3d5ffd5b806001600160a01b03163b5f036101a957604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516102069190610407565b5f60405180830381855af49150503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b509150915061025385838361027b565b95945050505050565b34156100785760405163b398979f60e01b815260040160405180910390fd5b6060826102905761028b826102da565b6102d3565b81511580156102a757506001600160a01b0384163b155b156102d057604051639996b31560e01b81526001600160a01b03851660048201526024016101a0565b50805b9392505050565b8051156102ea5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5f8085851115610311575f80fd5b8386111561031d575f80fd5b5050820193919092039150565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561034f575f80fd5b82356001600160a01b0381168114610365575f80fd5b9150602083013567ffffffffffffffff80821115610381575f80fd5b818501915085601f830112610394575f80fd5b8135818111156103a6576103a661032a565b604051601f8201601f19908116603f011681019083821181831017156103ce576103ce61032a565b816040528281528860208487010111156103e6575f80fd5b826020860160208301375f6020848301015280955050505050509250929050565b5f82518060208501845e5f92019182525091905056fea2646970667358221220e6e83e6b94bd1c904c450a0f7d266eefd5304d703d4586030e57945cfc84fa7d64736f6c63430008190033608060405234801561000f575f80fd5b506040516104e73803806104e783398101604081905261002e916100bb565b806001600160a01b03811661005c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100658161006c565b50506100e8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f602082840312156100cb575f80fd5b81516001600160a01b03811681146100e1575f80fd5b9392505050565b6103f2806100f55f395ff3fe608060405260043610610049575f3560e01c8063715018a61461004d5780638da5cb5b146100635780639623609d1461008e578063ad3cb1cc146100a1578063f2fde38b146100de575b5f80fd5b348015610058575f80fd5b506100616100fd565b005b34801561006e575f80fd5b505f546040516001600160a01b0390911681526020015b60405180910390f35b61006161009c366004610260565b610110565b3480156100ac575f80fd5b506100d1604051806040016040528060058152602001640352e302e360dc1b81525081565b604051610085919061035d565b3480156100e9575f80fd5b506100616100f8366004610376565b61017b565b6101056101bd565b61010e5f6101e9565b565b6101186101bd565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906101489086908690600401610391565b5f604051808303818588803b15801561015f575f80fd5b505af1158015610171573d5f803e3d5ffd5b5050505050505050565b6101836101bd565b6001600160a01b0381166101b157604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6101ba816101e9565b50565b5f546001600160a01b0316331461010e5760405163118cdaa760e01b81523360048201526024016101a8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146101ba575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f805f60608486031215610272575f80fd5b833561027d81610238565b9250602084013561028d81610238565b9150604084013567ffffffffffffffff808211156102a9575f80fd5b818601915086601f8301126102bc575f80fd5b8135818111156102ce576102ce61024c565b604051601f8201601f19908116603f011681019083821181831017156102f6576102f661024c565b8160405282815289602084870101111561030e575f80fd5b826020860160208301375f6020848301015280955050505050509250925092565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61036f602083018461032f565b9392505050565b5f60208284031215610386575f80fd5b813561036f81610238565b6001600160a01b03831681526040602082018190525f906103b49083018461032f565b94935050505056fea2646970667358221220201f933fb8df9921a6b060f6eac1014eaca99e8d26d0affaeed42773b42d620f64736f6c63430008190033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103610100604052348015610010575f80fd5b5060405161239838038061239883398101604081905261002f9161006c565b6001600160a01b0393841660805291831660a052821660c0521660e0526100bd565b80516001600160a01b0381168114610067575f80fd5b919050565b5f805f806080858703121561007f575f80fd5b61008885610051565b935061009660208601610051565b92506100a460408601610051565b91506100b260608601610051565b905092959194509250565b60805160a05160c05160e05161224d61014b5f395f81816101030152610b5101525f818161018e0152610b2f01525f818161023b01526109f601525f8181610275015281816102a20152818161032401528181610409015281816105ec0152818161071001528181610754015281816107f90152818161094401528181610aff0152610c1b015261224d5ff3fe608060405234801561000f575f80fd5b50600436106100e5575f3560e01c806386fa944211610088578063c28ef59111610063578063c28ef59114610223578063d8a06f7314610236578063ef36bbde1461025d578063fbfa77cf14610270575f80fd5b806386fa9442146101c35780638e6adb03146101eb5780639cdf7ad8146101fe575f80fd5b8063413b4bab116100c3578063413b4bab1461016357806347e7ef2414610176578063563a96e11461018957806371f96211146101b0575f80fd5b806301ee7642146100e95780633e47158c146100fe578063402d267d14610142575b5f80fd5b6100fc6100f7366004611071565b610297565b005b6101257f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b610155610150366004611157565b610386565b604051908152602001610139565b6100fc610171366004611172565b6105e1565b6100fc6101843660046111d6565b610705565b6101257f000000000000000000000000000000000000000000000000000000000000000081565b6101256101be366004611157565b610877565b6101256101d1366004611157565b5f602081905290815260409020546001600160a01b031681565b6100fc6101f9366004611200565b6108de565b61021361020c36600461123f565b5f92915050565b6040519015158152602001610139565b610125610231366004611157565b610938565b6101257f000000000000000000000000000000000000000000000000000000000000000081565b61015561026b366004611157565b610c04565b6101257f000000000000000000000000000000000000000000000000000000000000000081565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102e85760405162461bcd60e51b81526004016102df90611276565b60405180910390fd5b5f818060200190518101906102fd91906112ba565b604051635d0b520560e01b81529091506001600160a01b03821690635d0b520590610352907f0000000000000000000000000000000000000000000000000000000000000000908990899089906004016112d5565b5f604051808303815f87803b158015610369575f80fd5b505af115801561037b573d5f803e3d5ffd5b505050505050505050565b5f80829050806001600160a01b03166348d3b7756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103c7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103eb919061131f565b801561047c575060405163794b15b760e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015282169063794b15b790602401602060405180830381865afa158015610456573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047a919061131f565b155b1561048957505f92915050565b806001600160a01b031663a1b122026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e9919061131f565b6104f657505f1992915050565b5f816001600160a01b031663bd49c35f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610533573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610557919061133e565b90505f826001600160a01b031663ecf708586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610596573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105ba919061133e565b9050818111156105d7576105ce8282611369565b95945050505050565b505f949350505050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146106295760405162461bcd60e51b81526004016102df90611276565b60405163f3fef3a360e01b81526001600160a01b038581166004830152602482018490525f919087169063f3fef3a39060440160408051808303815f875af1158015610677573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069b919061137c565b60405163c8c01a5560e01b81526001600160a01b03878116600483015260248201839052919350908716915063c8c01a55906044015f604051808303815f87803b1580156106e7575f80fd5b505af11580156106f9573d5f803e3d5ffd5b50505050505050505050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461074d5760405162461bcd60e51b81526004016102df90611276565b6107e282827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107ae573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d291906112ba565b6001600160a01b03169190610c8f565b6040516311f9fbc960e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390528316906347e7ef249060440160408051808303815f875af115801561084d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610871919061137c565b50505050565b5f816001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108d891906112ba565b92915050565b602081146108fe5760405162461bcd60e51b81526004016102df9061139e565b5f61090b82840184611157565b90506001600160a01b0381166109335760405162461bcd60e51b81526004016102df9061139e565b505050565b5f336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109b15760405162461bcd60e51b815260206004820152601c60248201527f53796d62696f746963416461707465723a206f6e6c79207661756c740000000060448201526064016102df565b506001600160a01b038082165f908152602081905260409020541680156109d757919050565b6040516302910f8b60e31b81526001600160a01b0383811660048301527f000000000000000000000000000000000000000000000000000000000000000016906314887c5890602401602060405180830381865afa158015610a3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a5f919061131f565b610abd5760405162461bcd60e51b815260206004820152602960248201527f53796d62696f746963416461707465723a20696e76616c69642073796d62696f6044820152681d1a58c81d985d5b1d60ba1b60648201526084016102df565b6040516bffffffffffffffffffffffff19606084901b16602082015260340160408051601f198184030181529082905280516020909101206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116602484015284166044830152907f0000000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009060640160408051601f198184030181529181526020820180516001600160e01b031663485cc95560e01b17905251610ba890610ff7565b610bb4939291906113e3565b8190604051809103905ff5905080158015610bd1573d5f803e3d5ffd5b506001600160a01b039283165f90815260208190526040902080546001600160a01b031916938216939093179092555090565b6040516359f769a960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f91908316906359f769a990602401602060405180830381865afa158015610c6b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108d8919061133e565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015610cdc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d00919061133e565b90506108718484610d118585611432565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610d628482610dc4565b61087157604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610dba908590610e61565b6108718482610e61565b5f805f846001600160a01b031684604051610ddf9190611445565b5f604051808303815f865af19150503d805f8114610e18576040519150601f19603f3d011682016040523d82523d5f602084013e610e1d565b606091505b5091509150818015610e47575080511580610e47575080806020019051810190610e47919061131f565b80156105ce5750505050506001600160a01b03163b151590565b5f610e756001600160a01b03841683610ec2565b905080515f14158015610e99575080806020019051810190610e97919061131f565b155b1561093357604051635274afe760e01b81526001600160a01b03841660048201526024016102df565b6060610ecf83835f610ed6565b9392505050565b606081471015610efb5760405163cd78605960e01b81523060048201526024016102df565b5f80856001600160a01b03168486604051610f169190611445565b5f6040518083038185875af1925050503d805f8114610f50576040519150601f19603f3d011682016040523d82523d5f602084013e610f55565b606091505b5091509150610f65868383610f6f565b9695505050505050565b606082610f8457610f7f82610fcb565b610ecf565b8151158015610f9b57506001600160a01b0384163b155b15610fc457604051639996b31560e01b81526001600160a01b03851660048201526024016102df565b5080610ecf565b805115610fdb5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b610dbc8061145c83390190565b6001600160a01b0381168114610ff4575f80fd5b5f8083601f840112611028575f80fd5b50813567ffffffffffffffff81111561103f575f80fd5b602083019150836020828501011115611056575f80fd5b9250929050565b634e487b7160e01b5f52604160045260245ffd5b5f805f8060608587031215611084575f80fd5b843561108f81611004565b9350602085013567ffffffffffffffff808211156110ab575f80fd5b6110b788838901611018565b909550935060408701359150808211156110cf575f80fd5b818701915087601f8301126110e2575f80fd5b8135818111156110f4576110f461105d565b604051601f8201601f19908116603f0116810190838211818310171561111c5761111c61105d565b816040528281528a6020848701011115611134575f80fd5b826020860160208301375f60208483010152809550505050505092959194509250565b5f60208284031215611167575f80fd5b8135610ecf81611004565b5f805f805f60a08688031215611186575f80fd5b853561119181611004565b945060208601356111a181611004565b935060408601356111b181611004565b92506060860135915060808601356111c881611004565b809150509295509295909350565b5f80604083850312156111e7575f80fd5b82356111f281611004565b946020939093013593505050565b5f8060208385031215611211575f80fd5b823567ffffffffffffffff811115611227575f80fd5b61123385828601611018565b90969095509350505050565b5f8060408385031215611250575f80fd5b823561125b81611004565b9150602083013561126b81611004565b809150509250929050565b60208082526024908201527f53796d62696f746963416461707465723a2064656c65676174652063616c6c206040820152636f6e6c7960e01b606082015260800190565b5f602082840312156112ca575f80fd5b8151610ecf81611004565b6001600160a01b0385811682528416602082015260606040820181905281018290525f828460808401375f608084840101526080601f19601f850116830101905095945050505050565b5f6020828403121561132f575f80fd5b81518015158114610ecf575f80fd5b5f6020828403121561134e575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b818103818111156108d8576108d8611355565b5f806040838503121561138d575f80fd5b505080516020909101519092909150565b60208082526025908201527f53796d62696f746963416461707465723a20696e76616c696420726577617264604082015264206461746160d81b606082015260800190565b5f60018060a01b0380861683528085166020840152506060604083015282518060608401528060208501608085015e5f608082850101526080601f19601f830116840101915050949350505050565b808201808211156108d8576108d8611355565b5f82518060208501845e5f92019182525091905056fe60a0604052604051610dbc380380610dbc8339810160408190526100229161036a565b828161002e828261008c565b50508160405161003d9061032e565b6001600160a01b039091168152602001604051809103905ff080158015610066573d5f803e3d5ffd5b506001600160a01b031660805261008461007f60805190565b6100ea565b50505061044b565b61009582610157565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100de576100d982826101d5565b505050565b6100e6610248565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6101295f80516020610d9c833981519152546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a161015481610269565b50565b806001600160a01b03163b5f0361019157604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f80846001600160a01b0316846040516101f19190610435565b5f60405180830381855af49150503d805f8114610229576040519150601f19603f3d011682016040523d82523d5f602084013e61022e565b606091505b50909250905061023f8583836102a6565b95945050505050565b34156102675760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b03811661029257604051633173bdd160e11b81525f6004820152602401610188565b805f80516020610d9c8339815191526101b4565b6060826102bb576102b682610305565b6102fe565b81511580156102d257506001600160a01b0384163b155b156102fb57604051639996b31560e01b81526001600160a01b0385166004820152602401610188565b50805b9392505050565b8051156103155780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6104e7806108b583390190565b80516001600160a01b0381168114610351575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f805f6060848603121561037c575f80fd5b6103858461033b565b92506103936020850161033b565b60408501519092506001600160401b03808211156103af575f80fd5b818601915086601f8301126103c2575f80fd5b8151818111156103d4576103d4610356565b604051601f8201601f19908116603f011681019083821181831017156103fc576103fc610356565b81604052828152896020848701011115610414575f80fd5b8260208601602083015e5f6020848301015280955050505050509250925092565b5f82518060208501845e5f920191825250919050565b6080516104536104625f395f601001526104535ff3fe608060405261000c61000e565b005b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361007a575f356001600160e01b03191663278f794360e11b14610070576040516334ad5dbb60e21b815260040160405180910390fd5b610078610082565b565b6100786100b0565b5f806100913660048184610303565b81019061009e919061033e565b915091506100ac82826100c0565b5050565b6100786100bb61011a565b610151565b6100c98261016f565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101125761010d82826101ea565b505050565b6100ac61025c565b5f61014c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f80375f80365f845af43d5f803e80801561016b573d5ff35b3d5ffd5b806001600160a01b03163b5f036101a957604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516102069190610407565b5f60405180830381855af49150503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b509150915061025385838361027b565b95945050505050565b34156100785760405163b398979f60e01b815260040160405180910390fd5b6060826102905761028b826102da565b6102d3565b81511580156102a757506001600160a01b0384163b155b156102d057604051639996b31560e01b81526001600160a01b03851660048201526024016101a0565b50805b9392505050565b8051156102ea5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5f8085851115610311575f80fd5b8386111561031d575f80fd5b5050820193919092039150565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561034f575f80fd5b82356001600160a01b0381168114610365575f80fd5b9150602083013567ffffffffffffffff80821115610381575f80fd5b818501915085601f830112610394575f80fd5b8135818111156103a6576103a661032a565b604051601f8201601f19908116603f011681019083821181831017156103ce576103ce61032a565b816040528281528860208487010111156103e6575f80fd5b826020860160208301375f6020848301015280955050505050509250929050565b5f82518060208501845e5f92019182525091905056fea2646970667358221220e6e83e6b94bd1c904c450a0f7d266eefd5304d703d4586030e57945cfc84fa7d64736f6c63430008190033608060405234801561000f575f80fd5b506040516104e73803806104e783398101604081905261002e916100bb565b806001600160a01b03811661005c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100658161006c565b50506100e8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f602082840312156100cb575f80fd5b81516001600160a01b03811681146100e1575f80fd5b9392505050565b6103f2806100f55f395ff3fe608060405260043610610049575f3560e01c8063715018a61461004d5780638da5cb5b146100635780639623609d1461008e578063ad3cb1cc146100a1578063f2fde38b146100de575b5f80fd5b348015610058575f80fd5b506100616100fd565b005b34801561006e575f80fd5b505f546040516001600160a01b0390911681526020015b60405180910390f35b61006161009c366004610260565b610110565b3480156100ac575f80fd5b506100d1604051806040016040528060058152602001640352e302e360dc1b81525081565b604051610085919061035d565b3480156100e9575f80fd5b506100616100f8366004610376565b61017b565b6101056101bd565b61010e5f6101e9565b565b6101186101bd565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906101489086908690600401610391565b5f604051808303818588803b15801561015f575f80fd5b505af1158015610171573d5f803e3d5ffd5b5050505050505050565b6101836101bd565b6001600160a01b0381166101b157604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6101ba816101e9565b50565b5f546001600160a01b0316331461010e5760405163118cdaa760e01b81523360048201526024016101a8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146101ba575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f805f60608486031215610272575f80fd5b833561027d81610238565b9250602084013561028d81610238565b9150604084013567ffffffffffffffff808211156102a9575f80fd5b818601915086601f8301126102bc575f80fd5b8135818111156102ce576102ce61024c565b604051601f8201601f19908116603f011681019083821181831017156102f6576102f661024c565b8160405282815289602084870101111561030e575f80fd5b826020860160208301375f6020848301015280955050505050509250925092565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61036f602083018461032f565b9392505050565b5f60208284031215610386575f80fd5b813561036f81610238565b6001600160a01b03831681526040602082018190525f906103b49083018461032f565b94935050505056fea2646970667358221220201f933fb8df9921a6b060f6eac1014eaca99e8d26d0affaeed42773b42d620f64736f6c63430008190033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a26469706673582212206e9e76c911f1ae16b5758eb33a466666f70a881317a30d3f2698910ad1c2892664736f6c63430008190033a264697066735822122051c8e9164a1524a68f56082963d17725caf8b912821f5bf2bb530979754b454a64736f6c63430008190033
Verified Source Code Partial Match
Compiler: v0.8.25+commit.b61c2a91
EVM: cancun
Optimization: Yes (200 runs)
MultiVaultDeployScript.sol 154 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import "../../src/adapters/SymbioticAdapter.sol";
import "../../src/strategies/RatiosStrategy.sol";
import "../../src/vaults/MultiVault.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
contract MultiVaultDeployScript {
address public immutable symbioticVaultFactory;
RatiosStrategy public immutable strategy;
address public immutable multiVaultImplementation;
address public immutable symbioticWithdrawalQueueImplementation;
constructor(
address symbioticVaultFactory_,
address strategy_,
address multiVaultImplementation_,
address symbioticWithdrawalQueueImplementation_
) {
symbioticVaultFactory = symbioticVaultFactory_;
strategy = RatiosStrategy(strategy_);
multiVaultImplementation = multiVaultImplementation_;
symbioticWithdrawalQueueImplementation = symbioticWithdrawalQueueImplementation_;
}
struct DeployParams {
// actors
address admin;
address proxyAdmin;
address curator;
// external contracts
address symbioticVault;
address depositWrapper;
address asset;
address defaultCollateral;
// vault setup
uint256 limit;
bool depositPause;
bool withdrawalPause;
string name;
string symbol;
// strategy setup
uint64 minRatioD18;
uint64 maxRatioD18;
// salt
bytes32 salt;
}
struct Deployment {
address multiVault;
address symbioticAdapter;
DeployParams params;
}
mapping(bytes32 salt => Deployment) private _deployments;
function calculateSalt(DeployParams calldata params) public pure returns (bytes32) {
return keccak256(abi.encode(params));
}
function deployments(bytes32 salt) public view returns (Deployment memory) {
return _deployments[salt];
}
function deploy(DeployParams calldata params)
external
returns (MultiVault multiVault, address symbioticAdapter, DeployParams memory)
{
bytes32 salt = calculateSalt(params);
multiVault = MultiVault(
address(
new TransparentUpgradeableProxy{salt: salt}(
multiVaultImplementation, params.proxyAdmin, new bytes(0)
)
)
);
symbioticAdapter = address(
new SymbioticAdapter{salt: salt}(
address(multiVault),
symbioticVaultFactory,
symbioticWithdrawalQueueImplementation,
params.proxyAdmin
)
);
IMultiVault.InitParams memory initParams = IMultiVault.InitParams({
admin: address(this),
limit: params.limit,
depositPause: params.depositPause,
withdrawalPause: params.withdrawalPause,
depositWhitelist: params.depositWrapper != address(0),
asset: params.asset,
name: params.name,
symbol: params.symbol,
depositStrategy: address(strategy),
withdrawalStrategy: address(strategy),
rebalanceStrategy: address(strategy),
defaultCollateral: params.defaultCollateral,
symbioticAdapter: symbioticAdapter,
eigenLayerAdapter: address(0),
erc4626Adapter: address(0)
});
multiVault.initialize(initParams);
if (params.depositWrapper != address(0)) {
bytes32 SET_DEPOSITOR_WHITELIST_STATUS_ROLE =
keccak256("SET_DEPOSITOR_WHITELIST_STATUS_ROLE");
multiVault.grantRole(SET_DEPOSITOR_WHITELIST_STATUS_ROLE, address(this));
multiVault.setDepositorWhitelistStatus(params.depositWrapper, true);
multiVault.renounceRole(SET_DEPOSITOR_WHITELIST_STATUS_ROLE, address(this));
}
// admin roles
multiVault.grantRole(multiVault.DEFAULT_ADMIN_ROLE(), params.admin);
multiVault.grantRole(multiVault.SET_FARM_ROLE(), params.admin);
// curator roles
multiVault.grantRole(multiVault.REBALANCE_ROLE(), params.curator);
multiVault.grantRole(multiVault.ADD_SUBVAULT_ROLE(), params.curator);
multiVault.grantRole(keccak256("SET_LIMIT_ROLE"), params.curator);
multiVault.grantRole(strategy.RATIOS_STRATEGY_SET_RATIOS_ROLE(), params.curator);
if (params.symbioticVault != address(0)) {
multiVault.grantRole(strategy.RATIOS_STRATEGY_SET_RATIOS_ROLE(), address(this));
multiVault.grantRole(multiVault.ADD_SUBVAULT_ROLE(), address(this));
multiVault.addSubvault(params.symbioticVault, IMultiVaultStorage.Protocol.SYMBIOTIC);
address[] memory subvaults = new address[](1);
subvaults[0] = address(params.symbioticVault);
IRatiosStrategy.Ratio[] memory ratios_ = new IRatiosStrategy.Ratio[](1);
ratios_[0] = IRatiosStrategy.Ratio(params.minRatioD18, params.maxRatioD18);
strategy.setRatios(address(multiVault), subvaults, ratios_);
multiVault.renounceRole(strategy.RATIOS_STRATEGY_SET_RATIOS_ROLE(), address(this));
multiVault.renounceRole(multiVault.ADD_SUBVAULT_ROLE(), address(this));
}
multiVault.renounceRole(multiVault.DEFAULT_ADMIN_ROLE(), address(this));
emit Deployed(address(multiVault), symbioticAdapter, salt, params);
_deployments[salt] = Deployment(address(multiVault), symbioticAdapter, params);
return (multiVault, symbioticAdapter, params);
}
event Deployed(
address indexed multiVault,
address indexed symbioticAdapter,
bytes32 indexed salt,
DeployParams deployParams
);
}
SymbioticAdapter.sol 121 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import "../interfaces/adapters/ISymbioticAdapter.sol";
import {SymbioticWithdrawalQueue} from "../queues/SymbioticWithdrawalQueue.sol";
contract SymbioticAdapter is ISymbioticAdapter {
using SafeERC20 for IERC20;
/// @inheritdoc IProtocolAdapter
address public immutable vault;
/// @inheritdoc ISymbioticAdapter
IRegistry public immutable vaultFactory;
address public immutable withdrawalQueueSingleton;
address public immutable proxyAdmin;
/// @inheritdoc ISymbioticAdapter
mapping(address symbioticVault => address withdrawalQueue) public withdrawalQueues;
constructor(
address vault_,
address vaultFactory_,
address withdrawalQueueSingleton_,
address proxyAdmin_
) {
vault = vault_;
vaultFactory = IRegistry(vaultFactory_);
withdrawalQueueSingleton = withdrawalQueueSingleton_;
proxyAdmin = proxyAdmin_;
}
/// @inheritdoc IProtocolAdapter
function maxDeposit(address symbioticVault) external view returns (uint256) {
ISymbioticVault vault_ = ISymbioticVault(symbioticVault);
if (vault_.depositWhitelist() && !vault_.isDepositorWhitelisted(vault)) {
return 0;
}
if (!vault_.isDepositLimit()) {
return type(uint256).max;
}
uint256 activeStake = vault_.activeStake();
uint256 limit = vault_.depositLimit();
if (limit > activeStake) {
return limit - activeStake;
}
return 0;
}
/// @inheritdoc IProtocolAdapter
function assetOf(address symbioticVault) external view returns (address) {
return ISymbioticVault(symbioticVault).collateral();
}
/// @inheritdoc IProtocolAdapter
function stakedAt(address symbioticVault) external view returns (uint256) {
return ISymbioticVault(symbioticVault).activeBalanceOf(vault);
}
/// @inheritdoc IProtocolAdapter
function handleVault(address symbioticVault) external returns (address withdrawalQueue) {
require(msg.sender == vault, "SymbioticAdapter: only vault");
withdrawalQueue = withdrawalQueues[symbioticVault];
if (withdrawalQueue != address(0)) {
return withdrawalQueue;
}
require(vaultFactory.isEntity(symbioticVault), "SymbioticAdapter: invalid symbiotic vault");
withdrawalQueue = address(
new TransparentUpgradeableProxy{salt: keccak256(abi.encodePacked(symbioticVault))}(
withdrawalQueueSingleton,
proxyAdmin,
abi.encodeCall(SymbioticWithdrawalQueue.initialize, (vault, symbioticVault))
)
);
withdrawalQueues[symbioticVault] = withdrawalQueue;
}
/// @inheritdoc IProtocolAdapter
function validateRewardData(bytes calldata data) external pure {
require(data.length == 32, "SymbioticAdapter: invalid reward data");
address symbioticFarm = abi.decode(data, (address));
require(symbioticFarm != address(0), "SymbioticAdapter: invalid reward data");
}
/// @inheritdoc IProtocolAdapter
function pushRewards(address rewardToken, bytes calldata farmData, bytes memory rewardData)
external
{
require(address(this) == vault, "SymbioticAdapter: delegate call only");
address symbioticFarm = abi.decode(rewardData, (address));
IStakerRewards(symbioticFarm).claimRewards(vault, address(rewardToken), farmData);
}
/// @inheritdoc IProtocolAdapter
function withdraw(
address symbioticVault,
address withdrawalQueue,
address receiver,
uint256 request,
address /* owner */
) external {
require(address(this) == vault, "SymbioticAdapter: delegate call only");
(, uint256 requestedShares) =
ISymbioticVault(symbioticVault).withdraw(withdrawalQueue, request);
ISymbioticWithdrawalQueue(withdrawalQueue).request(receiver, requestedShares);
}
/// @inheritdoc IProtocolAdapter
function deposit(address symbioticVault, uint256 assets) external {
require(address(this) == vault, "SymbioticAdapter: delegate call only");
IERC20(IERC4626(vault).asset()).safeIncreaseAllowance(symbioticVault, assets);
ISymbioticVault(symbioticVault).deposit(vault, assets);
}
/// @inheritdoc IProtocolAdapter
function areWithdrawalsPaused(address, /* symbioticVault */ address /* account */ )
external
view
returns (bool)
{}
}
RatiosStrategy.sol 320 lines
// // SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import "../interfaces/strategies/IRatiosStrategy.sol";
contract RatiosStrategy is IRatiosStrategy {
/// @inheritdoc IRatiosStrategy
uint256 public constant D18 = 1e18;
/// @inheritdoc IRatiosStrategy
bytes32 public constant RATIOS_STRATEGY_SET_RATIOS_ROLE =
keccak256("RATIOS_STRATEGY_SET_RATIOS_ROLE");
/// @inheritdoc IRatiosStrategy
mapping(address vault => mapping(address subvault => Ratio)) public ratios;
/// @inheritdoc IRatiosStrategy
function setRatios(address vault, address[] calldata subvaults, Ratio[] calldata ratios_)
external
{
require(
IAccessControl(vault).hasRole(RATIOS_STRATEGY_SET_RATIOS_ROLE, msg.sender),
"RatiosStrategy: forbidden"
);
require(
subvaults.length == ratios_.length,
"RatiosStrategy: subvaults and ratios length mismatch"
);
IMultiVault multiVault = IMultiVault(vault);
uint256 n = subvaults.length;
for (uint256 i = 0; i < n; i++) {
if (multiVault.indexOfSubvault(subvaults[i]) != 0) {
require(
ratios_[i].minRatioD18 <= ratios_[i].maxRatioD18
&& ratios_[i].maxRatioD18 <= D18,
"RatiosStrategy: invalid ratios"
);
} else {
require(
ratios_[i].minRatioD18 == 0 && ratios_[i].maxRatioD18 == 0,
"RatiosStrategy: invalid subvault"
);
}
}
mapping(address => Ratio) storage vaultRatios_ = ratios[vault];
for (uint256 i = 0; i < n; i++) {
vaultRatios_[subvaults[i]] = ratios_[i];
}
emit RatiosSet(vault, subvaults, ratios_);
}
/// @inheritdoc IRatiosStrategy
function calculateState(address vault, bool isDeposit, uint256 increment)
public
view
returns (Amounts[] memory state, uint256 liquid)
{
IMultiVault multiVault = IMultiVault(vault);
uint256 n = multiVault.subvaultsCount();
state = new Amounts[](n);
liquid = IERC20(IERC4626(vault).asset()).balanceOf(vault);
IDefaultCollateral collateral = multiVault.defaultCollateral();
if (address(collateral) != address(0)) {
liquid += collateral.balanceOf(vault);
}
uint256 totalAssets = liquid;
IMultiVaultStorage.Subvault memory subvault;
for (uint256 i = 0; i < n; i++) {
subvault = multiVault.subvaultAt(i);
IProtocolAdapter adapter = multiVault.adapterOf(subvault.protocol);
state[i].staked = adapter.stakedAt(subvault.vault);
if (!isDeposit && adapter.areWithdrawalsPaused(subvault.vault, vault)) {
revert("RatiosStrategy: withdrawals paused");
}
if (subvault.withdrawalQueue != address(0)) {
state[i].claimable =
IWithdrawalQueue(subvault.withdrawalQueue).claimableAssetsOf(vault);
state[i].pending = IWithdrawalQueue(subvault.withdrawalQueue).pendingAssetsOf(vault);
totalAssets += state[i].staked + state[i].pending + state[i].claimable;
} else {
totalAssets += state[i].staked;
}
uint256 maxDeposit = adapter.maxDeposit(subvault.vault);
if (type(uint256).max - state[i].staked > maxDeposit) {
state[i].max = maxDeposit + state[i].staked;
} else {
state[i].max = type(uint256).max;
}
}
totalAssets = isDeposit ? totalAssets + increment : totalAssets - increment;
mapping(address => Ratio) storage vaultRatios_ = ratios[vault];
for (uint256 i = 0; i < n; i++) {
Ratio memory ratio = vaultRatios_[multiVault.subvaultAt(i).vault];
if (ratio.maxRatioD18 == 0) {
state[i].max = 0;
state[i].min = 0;
} else {
state[i].max = Math.min(state[i].max, (totalAssets * ratio.maxRatioD18) / D18);
state[i].min = Math.min(state[i].max, (totalAssets * ratio.minRatioD18) / D18);
}
}
}
/// @inheritdoc IDepositStrategy
function calculateDepositAmounts(address vault, uint256 amount)
external
view
override
returns (DepositData[] memory data)
{
(Amounts[] memory state,) = calculateState(vault, true, amount);
uint256 n = state.length;
for (uint256 i = 0; i < n; i++) {
uint256 assets_ = state[i].staked;
if (state[i].min > assets_) {
state[i].min -= assets_;
state[i].max -= assets_;
} else if (state[i].max > assets_) {
state[i].min = 0;
state[i].max -= assets_;
} else {
state[i].min = 0;
state[i].max = 0;
}
}
data = new DepositData[](n);
for (uint256 i = 0; i < n && amount != 0; i++) {
data[i].subvaultIndex = i;
if (state[i].min == 0) {
continue;
}
uint256 assets_ = Math.min(state[i].min, amount);
state[i].max -= assets_;
amount -= assets_;
data[i].deposit = assets_;
}
for (uint256 i = 0; i < n && amount != 0; i++) {
if (state[i].max == 0) {
continue;
}
uint256 assets_ = Math.min(state[i].max, amount);
amount -= assets_;
data[i].deposit += assets_;
}
uint256 count = 0;
for (uint256 i = 0; i < n; i++) {
if (data[i].deposit != 0) {
if (count != i) {
data[count] = data[i];
}
count++;
}
}
assembly {
mstore(data, count)
}
}
/// @inheritdoc IWithdrawalStrategy
function calculateWithdrawalAmounts(address vault, uint256 amount)
external
view
override
returns (WithdrawalData[] memory data)
{
(Amounts[] memory state, uint256 liquid) = calculateState(vault, false, amount);
if (amount <= liquid) {
return data;
}
amount -= liquid;
uint256 n = state.length;
data = new WithdrawalData[](n);
for (uint256 i = 0; i < n && amount != 0; i++) {
data[i].subvaultIndex = i;
if (state[i].staked > state[i].max) {
uint256 extra = state[i].staked - state[i].max;
if (extra > amount) {
data[i].staked = amount;
amount = 0;
} else {
data[i].staked = extra;
amount -= extra;
}
state[i].staked -= data[i].staked;
}
}
for (uint256 i = 0; i < n && amount != 0; i++) {
if (state[i].staked > state[i].min) {
uint256 allowed = state[i].staked - state[i].min;
if (allowed > amount) {
data[i].staked += amount;
amount = 0;
} else {
data[i].staked += allowed;
amount -= allowed;
state[i].staked -= allowed;
}
}
}
for (uint256 i = 0; i < n && amount != 0; i++) {
if (state[i].pending > 0) {
if (state[i].pending > amount) {
data[i].pending += amount;
amount = 0;
} else {
data[i].pending += state[i].pending;
amount -= state[i].pending;
}
}
}
for (uint256 i = 0; i < n && amount != 0; i++) {
if (state[i].claimable > 0) {
if (state[i].claimable > amount) {
data[i].claimable += amount;
amount = 0;
} else {
data[i].claimable += state[i].claimable;
amount -= state[i].claimable;
}
}
}
for (uint256 i = 0; i < n && amount != 0; i++) {
uint256 staked = state[i].staked;
if (staked > 0) {
if (staked > amount) {
data[i].staked += amount;
amount = 0;
} else {
data[i].staked += staked;
amount -= staked;
}
}
}
uint256 count = 0;
for (uint256 i = 0; i < n; i++) {
if (data[i].staked + data[i].pending + data[i].claimable != 0) {
if (count != i) {
data[count] = data[i];
}
count++;
}
}
assembly {
mstore(data, count)
}
}
/// @inheritdoc IRebalanceStrategy
function calculateRebalanceAmounts(address vault)
external
view
override
returns (RebalanceData[] memory data)
{
(Amounts[] memory state, uint256 liquid) = calculateState(vault, false, 0);
uint256 n = state.length;
data = new RebalanceData[](n);
uint256 totalRequired = 0;
uint256 pending = 0;
for (uint256 i = 0; i < n; i++) {
data[i].subvaultIndex = i;
data[i].claimable = state[i].claimable;
liquid += state[i].claimable;
pending += state[i].pending;
if (state[i].staked > state[i].max) {
data[i].staked = state[i].staked - state[i].max;
pending += data[i].staked;
state[i].staked = state[i].max;
}
if (state[i].min > state[i].staked) {
totalRequired += state[i].min - state[i].staked;
}
}
if (totalRequired > liquid + pending) {
uint256 unstake = totalRequired - liquid - pending;
for (uint256 i = 0; i < n && unstake > 0; i++) {
if (state[i].staked > state[i].min) {
uint256 allowed = state[i].staked - state[i].min;
if (allowed > unstake) {
data[i].staked += unstake;
unstake = 0;
} else {
data[i].staked += allowed;
unstake -= allowed;
}
}
}
}
for (uint256 i = 0; i < n && liquid > 0; i++) {
if (state[i].staked < state[i].min) {
uint256 required = state[i].min - state[i].staked;
if (required > liquid) {
data[i].deposit = liquid;
liquid = 0;
} else {
data[i].deposit = required;
liquid -= required;
state[i].max -= data[i].deposit;
}
}
}
for (uint256 i = 0; i < n && liquid > 0; i++) {
if (state[i].staked < state[i].max) {
uint256 allowed = state[i].max - state[i].staked;
if (allowed > liquid) {
data[i].deposit += liquid;
liquid = 0;
} else {
data[i].deposit += allowed;
liquid -= allowed;
}
}
}
}
}
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);
}
}
TransparentUpgradeableProxy.sol 116 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.20;
import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {ProxyAdmin} from "./ProxyAdmin.sol";
/**
* @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
* does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
* mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
* include them in the ABI so this interface must be used to interact with it.
*/
interface ITransparentUpgradeableProxy is IERC1967 {
function upgradeToAndCall(address, bytes calldata) external payable;
}
/**
* @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
* 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
* the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
* the proxy admin cannot fallback to the target implementation.
*
* These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
* dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
* call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
* allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
* interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
*
* NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
* inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
* mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
* fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
* implementation.
*
* NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
* meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
*
* IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
* immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
* overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
* undesirable state where the admin slot is different from the actual admin.
*
* WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
* compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
* function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
* could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
*/
contract TransparentUpgradeableProxy is ERC1967Proxy {
// An immutable address for the admin to avoid unnecessary SLOADs before each call
// at the expense of removing the ability to change the admin once it's set.
// This is acceptable if the admin is always a ProxyAdmin instance or similar contract
// with its own ability to transfer the permissions to another account.
address private immutable _admin;
/**
* @dev The proxy caller is the current admin, and can't fallback to the proxy target.
*/
error ProxyDeniedAdminAccess();
/**
* @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
* backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
* {ERC1967Proxy-constructor}.
*/
constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
_admin = address(new ProxyAdmin(initialOwner));
// Set the storage value and emit an event for ERC-1967 compatibility
ERC1967Utils.changeAdmin(_proxyAdmin());
}
/**
* @dev Returns the admin of this proxy.
*/
function _proxyAdmin() internal virtual returns (address) {
return _admin;
}
/**
* @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
*/
function _fallback() internal virtual override {
if (msg.sender == _proxyAdmin()) {
if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
revert ProxyDeniedAdminAccess();
} else {
_dispatchUpgradeToAndCall();
}
} else {
super._fallback();
}
}
/**
* @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
function _dispatchUpgradeToAndCall() private {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
ERC1967Utils.upgradeToAndCall(newImplementation, data);
}
}
ISymbioticAdapter.sol 22 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import {ISymbioticWithdrawalQueue} from "../queues/ISymbioticWithdrawalQueue.sol";
import {IProtocolAdapter} from "./IProtocolAdapter.sol";
import {IERC20, IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {TransparentUpgradeableProxy} from
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IRegistry} from "@symbiotic/core/interfaces/common/IRegistry.sol";
import {IVault as ISymbioticVault} from "@symbiotic/core/interfaces/vault/IVault.sol";
import {IStakerRewards} from "@symbiotic/rewards/interfaces/stakerRewards/IStakerRewards.sol";
interface ISymbioticAdapter is IProtocolAdapter {
function withdrawalQueues(address symbioticVault)
external
view
returns (address withdrawalQueue);
function vaultFactory() external view returns (IRegistry);
}
SymbioticWithdrawalQueue.sol 347 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import "../interfaces/queues/ISymbioticWithdrawalQueue.sol";
contract SymbioticWithdrawalQueue is ISymbioticWithdrawalQueue, Initializable {
using SafeERC20 for IERC20;
using Math for uint256;
/// @inheritdoc IWithdrawalQueue
address public immutable claimer;
/// @inheritdoc ISymbioticWithdrawalQueue
address public vault;
/// @inheritdoc ISymbioticWithdrawalQueue
ISymbioticVault public symbioticVault;
/// @inheritdoc ISymbioticWithdrawalQueue
address public collateral;
mapping(uint256 epoch => EpochData data) private _epochData;
mapping(address account => AccountData data) private _accountData;
constructor(address claimer_) {
claimer = claimer_;
_disableInitializers();
}
function initialize(address vault_, address symbioticVault_) external initializer {
__init_SymbioticWithdrawalQueue(vault_, symbioticVault_);
}
function __init_SymbioticWithdrawalQueue(address vault_, address symbioticVault_)
internal
onlyInitializing
{
vault = vault_;
symbioticVault = ISymbioticVault(symbioticVault_);
collateral = ISymbioticVault(symbioticVault_).collateral();
}
/// @inheritdoc ISymbioticWithdrawalQueue
function getCurrentEpoch() public view returns (uint256) {
return symbioticVault.currentEpoch();
}
/// @inheritdoc ISymbioticWithdrawalQueue
function getAccountData(address account)
external
view
returns (
uint256 sharesToClaimPrev,
uint256 sharesToClaim,
uint256 claimableAssets,
uint256 claimEpoch
)
{
AccountData storage accountData = _accountData[account];
claimEpoch = accountData.claimEpoch;
sharesToClaimPrev = claimEpoch == 0 ? 0 : accountData.sharesToClaim[claimEpoch - 1];
sharesToClaim = accountData.sharesToClaim[claimEpoch];
claimableAssets = accountData.claimableAssets;
}
/// @inheritdoc ISymbioticWithdrawalQueue
function getEpochData(uint256 epoch) external view returns (EpochData memory) {
return _epochData[epoch];
}
/// @inheritdoc ISymbioticWithdrawalQueue
function pendingAssets() public view returns (uint256) {
uint256 epoch = getCurrentEpoch();
address this_ = address(this);
ISymbioticVault symbioticVault_ = symbioticVault;
return symbioticVault_.withdrawalsOf(epoch, this_)
+ symbioticVault_.withdrawalsOf(epoch + 1, this_);
}
/// @inheritdoc ISymbioticWithdrawalQueue
function pendingAssetsOf(address account) public view returns (uint256 assets) {
uint256 epoch = getCurrentEpoch();
AccountData storage accountData = _accountData[account];
assets += _withdrawalsOf(epoch, accountData.sharesToClaim[epoch]);
epoch += 1;
assets += _withdrawalsOf(epoch, accountData.sharesToClaim[epoch]);
}
/// @inheritdoc ISymbioticWithdrawalQueue
function claimableAssetsOf(address account) public view returns (uint256 assets) {
AccountData storage accountData = _accountData[account];
assets = accountData.claimableAssets;
uint256 currentEpoch = getCurrentEpoch();
uint256 epoch = accountData.claimEpoch;
if (epoch > 0 && _isClaimableInSymbiotic(epoch - 1, currentEpoch)) {
assets += _claimable(accountData, epoch - 1);
}
if (_isClaimableInSymbiotic(epoch, currentEpoch)) {
assets += _claimable(accountData, epoch);
}
}
/// @inheritdoc ISymbioticWithdrawalQueue
function request(address account, uint256 amount) external {
require(msg.sender == vault, "SymbioticWithdrawalQueue: forbidden");
if (amount == 0) {
return;
}
AccountData storage accountData = _accountData[account];
uint256 epoch = getCurrentEpoch();
_handlePendingEpochs(accountData, epoch);
epoch = epoch + 1;
EpochData storage epochData = _epochData[epoch];
epochData.sharesToClaim += amount;
accountData.sharesToClaim[epoch] += amount;
accountData.claimEpoch = epoch;
emit WithdrawalRequested(account, epoch, amount);
}
/// @inheritdoc IWithdrawalQueue
function transferPendingAssets(address to, uint256 amount) external {
address from = msg.sender;
if (amount == 0 || from == to) {
return;
}
uint256 epoch = getCurrentEpoch();
uint256 nextEpoch = epoch + 1;
AccountData storage fromData = _accountData[from];
AccountData storage toData = _accountData[to];
_handlePendingEpochs(fromData, epoch);
_handlePendingEpochs(toData, epoch);
uint256 nextSharesToClaim = fromData.sharesToClaim[nextEpoch];
uint256 nextPending = _withdrawalsOf(nextEpoch, nextSharesToClaim);
if (nextPending >= amount) {
uint256 shares = nextSharesToClaim.mulDiv(amount, nextPending);
fromData.sharesToClaim[nextEpoch] -= shares;
toData.sharesToClaim[nextEpoch] += shares;
toData.claimEpoch = nextEpoch;
emit Transfer(from, to, nextEpoch, shares);
} else {
uint256 currentSharesToClaim = fromData.sharesToClaim[epoch];
uint256 currentPending = _withdrawalsOf(epoch, currentSharesToClaim);
require(
nextPending + currentPending >= amount,
"SymbioticWithdrawalQueue: insufficient pending assets"
);
if (nextSharesToClaim != 0) {
toData.sharesToClaim[nextEpoch] += nextSharesToClaim;
delete fromData.sharesToClaim[nextEpoch];
amount -= nextPending;
emit Transfer(from, to, nextEpoch, nextSharesToClaim);
}
uint256 shares = currentSharesToClaim.mulDiv(amount, currentPending);
fromData.sharesToClaim[epoch] = currentSharesToClaim - shares;
toData.sharesToClaim[epoch] += shares;
toData.claimEpoch = nextEpoch;
emit Transfer(from, to, epoch, shares);
}
}
/// @inheritdoc ISymbioticWithdrawalQueue
function pull(uint256 epoch) public {
require(
_isClaimableInSymbiotic(epoch, getCurrentEpoch()),
"SymbioticWithdrawalQueue: invalid epoch"
);
_pullFromSymbioticForEpoch(epoch);
}
/// @inheritdoc ISymbioticWithdrawalQueue
function claim(address account, address recipient, uint256 maxAmount)
external
returns (uint256 amount)
{
address sender = msg.sender;
require(sender == account || sender == claimer, "SymbioticWithdrawalQueue: forbidden");
AccountData storage accountData = _accountData[account];
_handlePendingEpochs(accountData, getCurrentEpoch());
amount = accountData.claimableAssets;
if (amount == 0) {
return 0;
}
if (amount <= maxAmount) {
accountData.claimableAssets = 0;
} else {
amount = maxAmount;
accountData.claimableAssets -= maxAmount;
}
if (amount != 0) {
IERC20(collateral).safeTransfer(recipient, amount);
}
emit Claimed(account, recipient, amount);
}
/// @inheritdoc ISymbioticWithdrawalQueue
function handlePendingEpochs(address account) public {
_handlePendingEpochs(_accountData[account], getCurrentEpoch());
}
/**
* @notice Calculates the amount of collateral that will be withdrawn for the given `shares` at the specified epoch.
* @param epoch The epoch number in the Symbiotic Vault.
* @param shares The amount of withdrawal shares.
* @return assets The corresponding amount of collateral that will be withdrawn based on the `shares`.
*/
function _withdrawalsOf(uint256 epoch, uint256 shares) private view returns (uint256) {
if (shares == 0) {
return 0;
}
return shares.mulDiv(
symbioticVault.withdrawalsOf(epoch, address(this)), _epochData[epoch].sharesToClaim
);
}
/**
* @notice Claims collateral from the Symbiotic Vault for the given account at the current and previous epochs.
* @dev This function updates the `epochData` and `accountData` mappings accordingly.
* @param accountData The storage struct containing specific claim data for the account.
* @param currentEpoch The current epoch in the Symbiotic Vault.
*
* @custom:requirements
* - If `claimEpoch` is zero, no claims are made for `epochEpoch` - 1.
* - If `claimEpoch` is non-zero, collateral is claimed for both `claimEpoch` and `claimEpoch`-1.
* - Ensures that both `claimEpoch` and `claimEpoch`-1 are claimable.
*
* @custom:effects
* - Emits `EpochClaimed` event when claims are successfully made.
*/
function _handlePendingEpochs(AccountData storage accountData, uint256 currentEpoch) private {
uint256 epoch = accountData.claimEpoch;
if (epoch > 0) {
_handlePendingEpoch(accountData, epoch - 1, currentEpoch);
}
_handlePendingEpoch(accountData, epoch, currentEpoch);
}
/**
* @notice Claims collateral from the Symbiotic Vault for the given `epoch`.
* @dev This function updates the `epochData` and `accountData` mappings.
* @param accountData The storage struct containing specific claim data for the account.
* @param epoch The epoch number from which the claim is made.
* @param currentEpoch The current epoch in the Symbiotic Vault.
*
* @custom:requirements
* - If `epoch` is not claimable, no claims are made.
*
* @custom:effects
* - Emits `EpochClaimed` event when claims are successfully made.
*/
function _handlePendingEpoch(
AccountData storage accountData,
uint256 epoch,
uint256 currentEpoch
) private {
if (!_isClaimableInSymbiotic(epoch, currentEpoch)) {
return;
}
uint256 shares = accountData.sharesToClaim[epoch];
if (shares == 0) {
return;
}
_pullFromSymbioticForEpoch(epoch);
EpochData storage epochData = _epochData[epoch];
uint256 assets = shares.mulDiv(epochData.claimableAssets, epochData.sharesToClaim);
epochData.sharesToClaim -= shares;
epochData.claimableAssets -= assets;
accountData.claimableAssets += assets;
delete accountData.sharesToClaim[epoch];
}
/**
* @notice Claims collateral from the Symbiotic Vault for the given `epoch`.
* @dev This function ensures that the withdrawals for the specified `epoch` are not claimed from the Symbiotic Vault.
* @param epoch The epoch number for which the collateral is being claimed.
*
* @custom:requirements
* - Checks that the collateral for the given `epoch` has not already been claimed.
* - Checks whether there are any claimable withdrawals for the given `epoch`.
*
* @custom:effects
* - Emits `EpochClaimed` event when the claim is successful.
*/
function _pullFromSymbioticForEpoch(uint256 epoch) private {
EpochData storage epochData = _epochData[epoch];
if (epochData.isClaimed) {
return;
}
epochData.isClaimed = true;
address this_ = address(this);
ISymbioticVault symbioticVault_ = symbioticVault;
if (symbioticVault_.isWithdrawalsClaimed(epoch, this_)) {
return;
}
if (symbioticVault_.withdrawalsOf(epoch, this_) == 0) {
return;
}
uint256 claimedAssets = symbioticVault_.claim(this_, epoch);
epochData.claimableAssets = claimedAssets;
emit EpochClaimed(epoch, claimedAssets);
}
/**
* @notice Returns the claimable collateral amount for a given `accountData` at a specific `epoch`.
* @param accountData The storage struct containing specific claim data for the account.
* @param epoch The epoch number at which the claim is being checked.
* @return The amount of claimable collateral corresponding to the given account's shares at the specified epoch.
*/
function _claimable(AccountData storage accountData, uint256 epoch)
private
view
returns (uint256)
{
uint256 shares = accountData.sharesToClaim[epoch];
if (shares == 0) {
return 0;
}
EpochData storage epochData = _epochData[epoch];
if (epochData.isClaimed) {
return shares.mulDiv(epochData.claimableAssets, epochData.sharesToClaim);
}
return _withdrawalsOf(epoch, shares);
}
/**
* @notice Determines whether the given `epoch` is claimable based on the current epoch.
* @param epoch The epoch number to check.
* @param currentEpoch The current epoch in the Symbiotic Vault.
* @return True if the epoch is claimable (i.e., it is less than the current epoch), false otherwise.
*/
function _isClaimableInSymbiotic(uint256 epoch, uint256 currentEpoch)
private
pure
returns (bool)
{
return epoch < currentEpoch;
}
}
IRatiosStrategy.sol 50 lines
// // SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import {IDefaultCollateral, IERC20} from "../tokens/IDefaultCollateral.sol";
import {
IMultiVault,
IMultiVaultStorage,
IProtocolAdapter,
IWithdrawalQueue
} from "../vaults/IMultiVault.sol";
import {IDepositStrategy} from "./IDepositStrategy.sol";
import {IRebalanceStrategy} from "./IRebalanceStrategy.sol";
import {IWithdrawalStrategy} from "./IWithdrawalStrategy.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
interface IRatiosStrategy is IDepositStrategy, IWithdrawalStrategy, IRebalanceStrategy {
struct Ratio {
uint64 minRatioD18;
uint64 maxRatioD18;
}
struct Amounts {
uint256 min;
uint256 max;
uint256 claimable;
uint256 pending;
uint256 staked;
}
function D18() external view returns (uint256);
function RATIOS_STRATEGY_SET_RATIOS_ROLE() external view returns (bytes32);
function ratios(address vault, address subvault)
external
view
returns (uint64 minRatioD18, uint64 maxRatioD18);
function setRatios(address vault, address[] calldata subvaults, Ratio[] calldata ratios)
external;
function calculateState(address vault, bool isDeposit, uint256 increment)
external
view
returns (Amounts[] memory state, uint256 liquid);
event RatiosSet(address indexed vault, address[] subvaults, Ratio[] ratios);
}
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
}
}
}
ERC1967Utils.sol 193 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}
ERC1967Proxy.sol 40 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
pragma solidity ^0.8.20;
import {Proxy} from "../Proxy.sol";
import {ERC1967Utils} from "./ERC1967Utils.sol";
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
* encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
constructor(address implementation, bytes memory _data) payable {
ERC1967Utils.upgradeToAndCall(implementation, _data);
}
/**
* @dev Returns the current implementation address.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function _implementation() internal view virtual override returns (address) {
return ERC1967Utils.getImplementation();
}
}
IERC1967.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}
ProxyAdmin.sol 45 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.20;
import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol";
import {Ownable} from "../../access/Ownable.sol";
/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract ProxyAdmin is Ownable {
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
* and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev Sets the initial owner who can perform upgrades.
*/
constructor(address initialOwner) Ownable(initialOwner) {}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
* See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
* - If `data` is empty, `msg.value` must be zero.
*/
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}
ISymbioticWithdrawalQueue.sol 162 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;
import {IWithdrawalQueue} from "./IWithdrawalQueue.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IVault as ISymbioticVault} from "@symbiotic/core/interfaces/vault/IVault.sol";
/**
* @title ISymbioticWithdrawalQueue
* @notice Interface to handle the withdrawal process from the Symbiotic Vault.
* @dev This interface is an extension of IWithdrawalQueue for interacting specifically with the Symbiotic Vault.
*/
interface ISymbioticWithdrawalQueue is IWithdrawalQueue {
/**
* @notice Struct to hold epoch-related data.
* @param isClaimed Indicates whether the epoch has been claimed.
* @param sharesToClaim The amount of shares to be claimed.
* @param claimableAssets The amount of assets that can be claimed.
*/
struct EpochData {
bool isClaimed;
uint256 sharesToClaim;
uint256 claimableAssets;
}
/**
* @notice Struct to store account-related data for withdrawals.
* @param sharesToClaim A mapping of epochs to shares to be claimed for a given epoch.
* @param claimableAssets The total amount of assets that can be claimed.
* @param claimEpoch The most recent epoch requested for withdrawal.
*/
struct AccountData {
mapping(uint256 => uint256) sharesToClaim;
uint256 claimableAssets;
uint256 claimEpoch;
}
/**
* @notice Returns the address of the associated MellowSymbioticVault.
* @return vault The address of the MellowSymbioticVault.
*/
function vault() external view returns (address);
/**
* @notice Returns the address of the associated Symbiotic Vault.
* @return symbioticVault The address of the Symbiotic Vault.
*/
function symbioticVault() external view returns (ISymbioticVault);
/**
* @notice Returns the address of the collateral token used by the vault.
* @dev The collateral token is the same as the vault's asset.
* @return collateralAddress The address of the collateral token.
*/
function collateral() external view returns (address);
/**
* @notice Returns the current epoch of the Symbiotic Vault.
* @return currentEpoch The current epoch of the Symbiotic Vault.
*/
function getCurrentEpoch() external view returns (uint256);
/**
* @notice Returns the data for a specific account.
* @param account The address of the account to retrieve data for.
* @return sharesToClaimPrev The amount of shares to claim for the epoch before the last requested epoch.
* @return sharesToClaim The amount of shares to claim for the last requested epoch.
* @return claimableAssets The total amount of assets that can be claimed.
* @return claimEpoch The most recent epoch requested for withdrawal.
*/
function getAccountData(address account)
external
view
returns (
uint256 sharesToClaimPrev,
uint256 sharesToClaim,
uint256 claimableAssets,
uint256 claimEpoch
);
/**
* @notice Returns the data for a specific epoch.
* @param epoch The epoch number to retrieve data for.
* @return epochData The data for the specified epoch.
*/
function getEpochData(uint256 epoch) external view returns (EpochData memory);
/**
* @notice Returns the total amount of pending assets awaiting withdrawal.
* @dev This amount may decrease due to slashing events in the Symbiotic Vault.
* @return assets The total amount of assets pending withdrawal.
*/
function pendingAssets() external view returns (uint256);
/**
* @notice Returns the amount of assets in the withdrawal queue for a specific account that cannot be claimed yet.
* @param account The address of the account.
* @return assets The amount of pending assets in the withdrawal queue for the account.
*/
function pendingAssetsOf(address account) external view returns (uint256 assets);
/**
* @notice Returns the amount of assets that can be claimed by an account.
* @param account The address of the account.
* @return assets The amount of assets claimable by the account.
*/
function claimableAssetsOf(address account) external view returns (uint256 assets);
/**
* @notice Requests the withdrawal of a specified amount of collateral for a given account.
* @param account The address of the account requesting the withdrawal.
* @param amount The amount of collateral to withdraw.
*
* @custom:requirements
* - `msg.sender` MUST be the vault.
* - `amount` MUST be greater than zero.
*
* @custom:effects
* - Emits a `WithdrawalRequested` event.
*/
function request(address account, uint256 amount) external;
/**
* @notice Claims assets from the Symbiotic Vault for a specified epoch to the Withdrawal Queue address.
* @param epoch The epoch number.
* @dev Emits an EpochClaimed event.
*/
function pull(uint256 epoch) external;
/**
* @notice Finalizes the withdrawal process for a specific account and transfers assets to the recipient.
* @param account The address of the account requesting the withdrawal.
* @param recipient The address of the recipient receiving the withdrawn assets.
* @param maxAmount The maximum amount of assets to withdraw.
* @return amount The actual amount of assets withdrawn.
*/
function claim(address account, address recipient, uint256 maxAmount)
external
returns (uint256 amount);
/**
* @notice Handles the pending epochs for a specific account and makes assets claimable for recent epochs.
* @param account The address of the account.
* @dev Emits an EpochClaimed event.
*/
function handlePendingEpochs(address account) external;
/// @notice Emitted when a withdrawal request is created.
event WithdrawalRequested(address indexed account, uint256 indexed epoch, uint256 amount);
/// @notice Emitted when assets are successfully claimed for a specific epoch.
event EpochClaimed(uint256 indexed epoch, uint256 claimedAssets);
/// @notice Emitted when assets are successfully withdrawn and transferred to a recipient.
event Claimed(address indexed account, address indexed recipient, uint256 amount);
/// @notice Emitted when pending assets are successfully transferred from one account to another.
event Transfer(address indexed from, address indexed to, uint256 indexed epoch, uint256 amount);
}
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);
}
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);
}
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;
}
}
IRegistry.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IRegistry {
error EntityNotExist();
/**
* @notice Emitted when an entity is added.
* @param entity address of the added entity
*/
event AddEntity(address indexed entity);
/**
* @notice Get if a given address is an entity.
* @param account address to check
* @return if the given address is an entity
*/
function isEntity(
address account
) external view returns (bool);
/**
* @notice Get a total number of entities.
* @return total number of entities added
*/
function totalEntities() external view returns (uint256);
/**
* @notice Get an entity given its index.
* @param index index of the entity to get
* @return address of the entity
*/
function entity(
uint256 index
) external view returns (address);
}
IVault.sol 319 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IMigratableEntity} from "../common/IMigratableEntity.sol";
import {IVaultStorage} from "./IVaultStorage.sol";
interface IVault is IMigratableEntity, IVaultStorage {
error AlreadyClaimed();
error AlreadySet();
error DelegatorAlreadyInitialized();
error DepositLimitReached();
error InsufficientClaim();
error InsufficientDeposit();
error InsufficientRedemption();
error InsufficientWithdrawal();
error InvalidAccount();
error InvalidCaptureEpoch();
error InvalidClaimer();
error InvalidCollateral();
error InvalidDelegator();
error InvalidEpoch();
error InvalidEpochDuration();
error InvalidLengthEpochs();
error InvalidOnBehalfOf();
error InvalidRecipient();
error InvalidSlasher();
error MissingRoles();
error NotDelegator();
error NotSlasher();
error NotWhitelistedDepositor();
error SlasherAlreadyInitialized();
error TooMuchRedeem();
error TooMuchWithdraw();
/**
* @notice Initial parameters needed for a vault deployment.
* @param collateral vault's underlying collateral
* @param burner vault's burner to issue debt to (e.g., 0xdEaD or some unwrapper contract)
* @param epochDuration duration of the vault epoch (it determines sync points for withdrawals)
* @param depositWhitelist if enabling deposit whitelist
* @param isDepositLimit if enabling deposit limit
* @param depositLimit deposit limit (maximum amount of the collateral that can be in the vault simultaneously)
* @param defaultAdminRoleHolder address of the initial DEFAULT_ADMIN_ROLE holder
* @param depositWhitelistSetRoleHolder address of the initial DEPOSIT_WHITELIST_SET_ROLE holder
* @param depositorWhitelistRoleHolder address of the initial DEPOSITOR_WHITELIST_ROLE holder
* @param isDepositLimitSetRoleHolder address of the initial IS_DEPOSIT_LIMIT_SET_ROLE holder
* @param depositLimitSetRoleHolder address of the initial DEPOSIT_LIMIT_SET_ROLE holder
*/
struct InitParams {
address collateral;
address burner;
uint48 epochDuration;
bool depositWhitelist;
bool isDepositLimit;
uint256 depositLimit;
address defaultAdminRoleHolder;
address depositWhitelistSetRoleHolder;
address depositorWhitelistRoleHolder;
address isDepositLimitSetRoleHolder;
address depositLimitSetRoleHolder;
}
/**
* @notice Hints for an active balance.
* @param activeSharesOfHint hint for the active shares of checkpoint
* @param activeStakeHint hint for the active stake checkpoint
* @param activeSharesHint hint for the active shares checkpoint
*/
struct ActiveBalanceOfHints {
bytes activeSharesOfHint;
bytes activeStakeHint;
bytes activeSharesHint;
}
/**
* @notice Emitted when a deposit is made.
* @param depositor account that made the deposit
* @param onBehalfOf account the deposit was made on behalf of
* @param amount amount of the collateral deposited
* @param shares amount of the active shares minted
*/
event Deposit(address indexed depositor, address indexed onBehalfOf, uint256 amount, uint256 shares);
/**
* @notice Emitted when a withdrawal is made.
* @param withdrawer account that made the withdrawal
* @param claimer account that needs to claim the withdrawal
* @param amount amount of the collateral withdrawn
* @param burnedShares amount of the active shares burned
* @param mintedShares amount of the epoch withdrawal shares minted
*/
event Withdraw(
address indexed withdrawer, address indexed claimer, uint256 amount, uint256 burnedShares, uint256 mintedShares
);
/**
* @notice Emitted when a claim is made.
* @param claimer account that claimed
* @param recipient account that received the collateral
* @param epoch epoch the collateral was claimed for
* @param amount amount of the collateral claimed
*/
event Claim(address indexed claimer, address indexed recipient, uint256 epoch, uint256 amount);
/**
* @notice Emitted when a batch claim is made.
* @param claimer account that claimed
* @param recipient account that received the collateral
* @param epochs epochs the collateral was claimed for
* @param amount amount of the collateral claimed
*/
event ClaimBatch(address indexed claimer, address indexed recipient, uint256[] epochs, uint256 amount);
/**
* @notice Emitted when a slash happens.
* @param amount amount of the collateral to slash
* @param captureTimestamp time point when the stake was captured
* @param slashedAmount real amount of the collateral slashed
*/
event OnSlash(uint256 amount, uint48 captureTimestamp, uint256 slashedAmount);
/**
* @notice Emitted when a deposit whitelist status is enabled/disabled.
* @param status if enabled deposit whitelist
*/
event SetDepositWhitelist(bool status);
/**
* @notice Emitted when a depositor whitelist status is set.
* @param account account for which the whitelist status is set
* @param status if whitelisted the account
*/
event SetDepositorWhitelistStatus(address indexed account, bool status);
/**
* @notice Emitted when a deposit limit status is enabled/disabled.
* @param status if enabled deposit limit
*/
event SetIsDepositLimit(bool status);
/**
* @notice Emitted when a deposit limit is set.
* @param limit deposit limit (maximum amount of the collateral that can be in the vault simultaneously)
*/
event SetDepositLimit(uint256 limit);
/**
* @notice Emitted when a delegator is set.
* @param delegator vault's delegator to delegate the stake to networks and operators
* @dev Can be set only once.
*/
event SetDelegator(address indexed delegator);
/**
* @notice Emitted when a slasher is set.
* @param slasher vault's slasher to provide a slashing mechanism to networks
* @dev Can be set only once.
*/
event SetSlasher(address indexed slasher);
/**
* @notice Check if the vault is fully initialized (a delegator and a slasher are set).
* @return if the vault is fully initialized
*/
function isInitialized() external view returns (bool);
/**
* @notice Get a total amount of the collateral that can be slashed.
* @return total amount of the slashable collateral
*/
function totalStake() external view returns (uint256);
/**
* @notice Get an active balance for a particular account at a given timestamp using hints.
* @param account account to get the active balance for
* @param timestamp time point to get the active balance for the account at
* @param hints hints for checkpoints' indexes
* @return active balance for the account at the timestamp
*/
function activeBalanceOfAt(
address account,
uint48 timestamp,
bytes calldata hints
) external view returns (uint256);
/**
* @notice Get an active balance for a particular account.
* @param account account to get the active balance for
* @return active balance for the account
*/
function activeBalanceOf(
address account
) external view returns (uint256);
/**
* @notice Get withdrawals for a particular account at a given epoch (zero if claimed).
* @param epoch epoch to get the withdrawals for the account at
* @param account account to get the withdrawals for
* @return withdrawals for the account at the epoch
*/
function withdrawalsOf(uint256 epoch, address account) external view returns (uint256);
/**
* @notice Get a total amount of the collateral that can be slashed for a given account.
* @param account account to get the slashable collateral for
* @return total amount of the account's slashable collateral
*/
function slashableBalanceOf(
address account
) external view returns (uint256);
/**
* @notice Deposit collateral into the vault.
* @param onBehalfOf account the deposit is made on behalf of
* @param amount amount of the collateral to deposit
* @return depositedAmount real amount of the collateral deposited
* @return mintedShares amount of the active shares minted
*/
function deposit(
address onBehalfOf,
uint256 amount
) external returns (uint256 depositedAmount, uint256 mintedShares);
/**
* @notice Withdraw collateral from the vault (it will be claimable after the next epoch).
* @param claimer account that needs to claim the withdrawal
* @param amount amount of the collateral to withdraw
* @return burnedShares amount of the active shares burned
* @return mintedShares amount of the epoch withdrawal shares minted
*/
function withdraw(address claimer, uint256 amount) external returns (uint256 burnedShares, uint256 mintedShares);
/**
* @notice Redeem collateral from the vault (it will be claimable after the next epoch).
* @param claimer account that needs to claim the withdrawal
* @param shares amount of the active shares to redeem
* @return withdrawnAssets amount of the collateral withdrawn
* @return mintedShares amount of the epoch withdrawal shares minted
*/
function redeem(address claimer, uint256 shares) external returns (uint256 withdrawnAssets, uint256 mintedShares);
/**
* @notice Claim collateral from the vault.
* @param recipient account that receives the collateral
* @param epoch epoch to claim the collateral for
* @return amount amount of the collateral claimed
*/
function claim(address recipient, uint256 epoch) external returns (uint256 amount);
/**
* @notice Claim collateral from the vault for multiple epochs.
* @param recipient account that receives the collateral
* @param epochs epochs to claim the collateral for
* @return amount amount of the collateral claimed
*/
function claimBatch(address recipient, uint256[] calldata epochs) external returns (uint256 amount);
/**
* @notice Slash callback for burning collateral.
* @param amount amount to slash
* @param captureTimestamp time point when the stake was captured
* @return slashedAmount real amount of the collateral slashed
* @dev Only the slasher can call this function.
*/
function onSlash(uint256 amount, uint48 captureTimestamp) external returns (uint256 slashedAmount);
/**
* @notice Enable/disable deposit whitelist.
* @param status if enabling deposit whitelist
* @dev Only a DEPOSIT_WHITELIST_SET_ROLE holder can call this function.
*/
function setDepositWhitelist(
bool status
) external;
/**
* @notice Set a depositor whitelist status.
* @param account account for which the whitelist status is set
* @param status if whitelisting the account
* @dev Only a DEPOSITOR_WHITELIST_ROLE holder can call this function.
*/
function setDepositorWhitelistStatus(address account, bool status) external;
/**
* @notice Enable/disable deposit limit.
* @param status if enabling deposit limit
* @dev Only a IS_DEPOSIT_LIMIT_SET_ROLE holder can call this function.
*/
function setIsDepositLimit(
bool status
) external;
/**
* @notice Set a deposit limit.
* @param limit deposit limit (maximum amount of the collateral that can be in the vault simultaneously)
* @dev Only a DEPOSIT_LIMIT_SET_ROLE holder can call this function.
*/
function setDepositLimit(
uint256 limit
) external;
/**
* @notice Set a delegator.
* @param delegator vault's delegator to delegate the stake to networks and operators
* @dev Can be set only once.
*/
function setDelegator(
address delegator
) external;
/**
* @notice Set a slasher.
* @param slasher vault's slasher to provide a slashing mechanism to networks
* @dev Can be set only once.
*/
function setSlasher(
address slasher
) external;
}
IStakerRewards.sol 46 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IStakerRewards {
/**
* @notice Emitted when a reward is distributed.
* @param network network on behalf of which the reward is distributed
* @param token address of the token
* @param amount amount of tokens
* @param data some used data
*/
event DistributeRewards(address indexed network, address indexed token, uint256 amount, bytes data);
/**
* @notice Get a version of the staker rewards contract (different versions mean different interfaces).
* @return version of the staker rewards contract
* @dev Must return 1 for this one.
*/
function version() external view returns (uint64);
/**
* @notice Get an amount of rewards claimable by a particular account of a given token.
* @param token address of the token
* @param account address of the claimer
* @param data some data to use
* @return amount of claimable tokens
*/
function claimable(address token, address account, bytes calldata data) external view returns (uint256);
/**
* @notice Distribute rewards on behalf of a particular network using a given token.
* @param network network on behalf of which the reward to distribute
* @param token address of the token
* @param amount amount of tokens
* @param data some data to use
*/
function distributeRewards(address network, address token, uint256 amount, bytes calldata data) external;
/**
* @notice Claim rewards using a given token.
* @param recipient address of the tokens' recipient
* @param token address of the token
* @param data some data to use
*/
function claimRewards(address recipient, address token, bytes calldata data) external;
}
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;
}
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);
}
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;
}
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;
}
}
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
);
}
IBeacon.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
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();
}
}
}
StorageSlot.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
Proxy.sol 69 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
pragma solidity ^0.8.20;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback
* function and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
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);
}
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
}
}
}
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);
}
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);
}
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);
}
IMigratableEntity.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IMigratableEntity {
error AlreadyInitialized();
error NotFactory();
error NotInitialized();
/**
* @notice Get the factory's address.
* @return address of the factory
*/
function FACTORY() external view returns (address);
/**
* @notice Get the entity's version.
* @return version of the entity
* @dev Starts from 1.
*/
function version() external view returns (uint64);
/**
* @notice Initialize this entity contract by using a given data and setting a particular version and owner.
* @param initialVersion initial version of the entity
* @param owner initial owner of the entity
* @param data some data to use
*/
function initialize(uint64 initialVersion, address owner, bytes calldata data) external;
/**
* @notice Migrate this entity to a particular newer version using a given data.
* @param newVersion new version of the entity
* @param data some data to use
*/
function migrate(uint64 newVersion, bytes calldata data) external;
}
IVaultStorage.sol 233 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVaultStorage {
error InvalidTimestamp();
error NoPreviousEpoch();
/**
* @notice Get a deposit whitelist enabler/disabler's role.
* @return identifier of the whitelist enabler/disabler role
*/
function DEPOSIT_WHITELIST_SET_ROLE() external view returns (bytes32);
/**
* @notice Get a depositor whitelist status setter's role.
* @return identifier of the depositor whitelist status setter role
*/
function DEPOSITOR_WHITELIST_ROLE() external view returns (bytes32);
/**
* @notice Get a deposit limit enabler/disabler's role.
* @return identifier of the deposit limit enabler/disabler role
*/
function IS_DEPOSIT_LIMIT_SET_ROLE() external view returns (bytes32);
/**
* @notice Get a deposit limit setter's role.
* @return identifier of the deposit limit setter role
*/
function DEPOSIT_LIMIT_SET_ROLE() external view returns (bytes32);
/**
* @notice Get the delegator factory's address.
* @return address of the delegator factory
*/
function DELEGATOR_FACTORY() external view returns (address);
/**
* @notice Get the slasher factory's address.
* @return address of the slasher factory
*/
function SLASHER_FACTORY() external view returns (address);
/**
* @notice Get a vault collateral.
* @return address of the underlying collateral
*/
function collateral() external view returns (address);
/**
* @notice Get a burner to issue debt to (e.g., 0xdEaD or some unwrapper contract).
* @return address of the burner
*/
function burner() external view returns (address);
/**
* @notice Get a delegator (it delegates the vault's stake to networks and operators).
* @return address of the delegator
*/
function delegator() external view returns (address);
/**
* @notice Get if the delegator is initialized.
* @return if the delegator is initialized
*/
function isDelegatorInitialized() external view returns (bool);
/**
* @notice Get a slasher (it provides networks a slashing mechanism).
* @return address of the slasher
*/
function slasher() external view returns (address);
/**
* @notice Get if the slasher is initialized.
* @return if the slasher is initialized
*/
function isSlasherInitialized() external view returns (bool);
/**
* @notice Get a time point of the epoch duration set.
* @return time point of the epoch duration set
*/
function epochDurationInit() external view returns (uint48);
/**
* @notice Get a duration of the vault epoch.
* @return duration of the epoch
*/
function epochDuration() external view returns (uint48);
/**
* @notice Get an epoch at a given timestamp.
* @param timestamp time point to get the epoch at
* @return epoch at the timestamp
* @dev Reverts if the timestamp is less than the start of the epoch 0.
*/
function epochAt(
uint48 timestamp
) external view returns (uint256);
/**
* @notice Get a current vault epoch.
* @return current epoch
*/
function currentEpoch() external view returns (uint256);
/**
* @notice Get a start of the current vault epoch.
* @return start of the current epoch
*/
function currentEpochStart() external view returns (uint48);
/**
* @notice Get a start of the previous vault epoch.
* @return start of the previous epoch
* @dev Reverts if the current epoch is 0.
*/
function previousEpochStart() external view returns (uint48);
/**
* @notice Get a start of the next vault epoch.
* @return start of the next epoch
*/
function nextEpochStart() external view returns (uint48);
/**
* @notice Get if the deposit whitelist is enabled.
* @return if the deposit whitelist is enabled
*/
function depositWhitelist() external view returns (bool);
/**
* @notice Get if a given account is whitelisted as a depositor.
* @param account address to check
* @return if the account is whitelisted as a depositor
*/
function isDepositorWhitelisted(
address account
) external view returns (bool);
/**
* @notice Get if the deposit limit is set.
* @return if the deposit limit is set
*/
function isDepositLimit() external view returns (bool);
/**
* @notice Get a deposit limit (maximum amount of the active stake that can be in the vault simultaneously).
* @return deposit limit
*/
function depositLimit() external view returns (uint256);
/**
* @notice Get a total number of active shares in the vault at a given timestamp using a hint.
* @param timestamp time point to get the total number of active shares at
* @param hint hint for the checkpoint index
* @return total number of active shares at the timestamp
*/
function activeSharesAt(uint48 timestamp, bytes memory hint) external view returns (uint256);
/**
* @notice Get a total number of active shares in the vault.
* @return total number of active shares
*/
function activeShares() external view returns (uint256);
/**
* @notice Get a total amount of active stake in the vault at a given timestamp using a hint.
* @param timestamp time point to get the total active stake at
* @param hint hint for the checkpoint index
* @return total amount of active stake at the timestamp
*/
function activeStakeAt(uint48 timestamp, bytes memory hint) external view returns (uint256);
/**
* @notice Get a total amount of active stake in the vault.
* @return total amount of active stake
*/
function activeStake() external view returns (uint256);
/**
* @notice Get a total number of active shares for a particular account at a given timestamp using a hint.
* @param account account to get the number of active shares for
* @param timestamp time point to get the number of active shares for the account at
* @param hint hint for the checkpoint index
* @return number of active shares for the account at the timestamp
*/
function activeSharesOfAt(address account, uint48 timestamp, bytes memory hint) external view returns (uint256);
/**
* @notice Get a number of active shares for a particular account.
* @param account account to get the number of active shares for
* @return number of active shares for the account
*/
function activeSharesOf(
address account
) external view returns (uint256);
/**
* @notice Get a total amount of the withdrawals at a given epoch.
* @param epoch epoch to get the total amount of the withdrawals at
* @return total amount of the withdrawals at the epoch
*/
function withdrawals(
uint256 epoch
) external view returns (uint256);
/**
* @notice Get a total number of withdrawal shares at a given epoch.
* @param epoch epoch to get the total number of withdrawal shares at
* @return total number of withdrawal shares at the epoch
*/
function withdrawalShares(
uint256 epoch
) external view returns (uint256);
/**
* @notice Get a number of withdrawal shares for a particular account at a given epoch (zero if claimed).
* @param epoch epoch to get the number of withdrawal shares for the account at
* @param account account to get the number of withdrawal shares for
* @return number of withdrawal shares for the account at the epoch
*/
function withdrawalSharesOf(uint256 epoch, address account) external view returns (uint256);
/**
* @notice Get if the withdrawals are claimed for a particular account at a given epoch.
* @param epoch epoch to check the withdrawals for the account at
* @param account account to check the withdrawals for
* @return if the withdrawals are claimed for the account at the epoch
*/
function isWithdrawalsClaimed(uint256 epoch, address account) external view returns (bool);
}
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;
}
}
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;
}
}
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;
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
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;
}
}
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);
}
}
}
}
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;
}
}
}
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
calculateSalt 0xebfa3fdb → bytes32
deployments 0x37404017 → tuple
multiVaultImplementation 0x187a4ad9 → address
strategy 0xa8c62e76 → address
symbioticVaultFactory 0x23ee74fb → address
symbioticWithdrawalQueueImplementation 0x08e85a78 → address
Write Contract 1 functions
These functions modify contract state and require a wallet transaction to execute.
deploy 0xd741974a
tuple params
returns: address, address, tuple
Recent Transactions
No transactions found for this address