Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x4dfF3ABa0fbd61c332F38205ED4a8Ba42B4407d6
Balance 0 ETH
Nonce 1
Code Size 16943 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

16943 bytes
0x608060405234801561000f575f80fd5b506004361061044e575f3560e01c80637dc0d1d011610242578063b460af9411610140578063d905777e116100bf578063e63ab1e911610084578063e63ab1e914610c18578063e90baf1814610c3f578063eaed1d0714610c52578063ef8b30f714610c7f578063f5a23d8d14610c92575f80fd5b8063d905777e14610af8578063db006a7514610b23578063db8d55f114610b36578063db901a2a14610bdb578063dd62ed3e14610bee575f80fd5b8063c6e6f59211610105578063c6e6f59214610a67578063cdf5bba314610a7a578063ce95596014610aa7578063ce96cb7714610aba578063d505accf14610ae5575f80fd5b8063b460af94146109ee578063b6363cf214610a01578063b6b55f2514610a2e578063ba08765214610a41578063c63d75b614610a54575f80fd5b806395d89b41116101cc578063a8d5fd6511610191578063a8d5fd651461098a578063a9059cbb146109a2578063aa2f892d146109b5578063adc565e1146109c8578063b3d7f6b9146109db575f80fd5b806395d89b41146108b757806399998406146108bf5780639a6d1866146108d25780639af1d35a146108e9578063a0712d6814610977575f80fd5b80638456cb59116102125780638456cb591461084e578063860aefcf146108565780638da5cb5b1461086457806391d148541461087757806394bf804d146108a4575f80fd5b80637dc0d1d01461080c5780637ecebe001461081f5780637ed51b771461083e5780637f2d957514610846575f80fd5b80633644e5151161034f5780635c975abb116102d9578063711b58ff1161029e578063711b58ff1461076257806378d873281461077557806379ba5097146107ab5780637d41c86e146107b35780637d7b81b7146107c6575f80fd5b80635c975abb146107135780635dd912f514610720578063682c2058146107285780636e553f651461073057806370a0823114610743575f80fd5b8063402d267d1161031f578063402d267d146106b75780634cdad506146104e057806353a47bb7146106ca578063558a7297146106dd5780635843a5ad146106f0575f80fd5b80633644e5151461064c57806338d52e0f146106545780633cc87a201461067b5780633f4ba83a146106af575f80fd5b80631627540c116103db5780632aa38127116103a05780632aa38127146105c75780632d0777d8146105da5780632e1a7d4d146105ed578063313ce56714610600578063346080af14610639575f80fd5b80631627540c1461055a57806318160ddd1461056d578063186f03541461057657806323b872dd146105a157806326537b57146105b4575f80fd5b806307a2d13a1161042157806307a2d13a146104ba578063095ea7b3146104cd5780630a28a477146104e0578063126bd46c146104f3578063138e027c14610506575f80fd5b806301e1d1141461045257806301ffc9a71461046d578063025d7e331461049057806306fdde03146104a5575b5f80fd5b61045a610cbc565b6040519081526020015b60405180910390f35b61048061047b3660046137d5565b610d68565b6040519015158152602001610464565b6104a361049e36600461388c565b610d92565b005b6104ad610da6565b60405161046491906138a6565b61045a6104c83660046138f2565b610e31565b6104806104db366004613924565b610e5d565b61045a6104ee3660046138f2565b610ec8565b61045a61050136600461394c565b610f17565b61053a610514366004613976565b600d6020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610464565b6104a3610568366004613976565b610fd7565b61045a60025481565b601554610589906001600160a01b031681565b6040516001600160a01b039091168152602001610464565b6104806105af36600461398f565b611033565b61045a6105c23660046138f2565b61110d565b6104a36105d5366004613976565b611168565b6104a36105e83660046139c8565b611220565b61045a6105fb3660046138f2565b61122e565b6106277f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610464565b6104a36106473660046139ff565b61123a565b61045a6112b9565b6105897f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040805180820182525f8082526020918201528151808301909252600e548252600f54908201526040516104649190613a38565b6104a361130e565b61045a6106c5366004613976565b611320565b600754610589906001600160a01b031681565b6104806106eb366004613a4f565b61136f565b600e54600f546106fe919082565b60408051928352602083019190915201610464565b6009546104809060ff1681565b6104a36113fc565b61045a61140c565b61045a61073e36600461394c565b61147d565b61045a610751366004613976565b60036020525f908152604090205481565b610480610770366004613a77565b611540565b601954601a5461078c916001600160a01b03169082565b604080516001600160a01b039093168352602083019190915201610464565b6104a36118d6565b61045a6107c1366004613b47565b611998565b6107d96107d4366004613976565b6119f7565b60405161046491908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b601654610589906001600160a01b031681565b61045a61082d366004613976565b60056020525f908152604090205481565b6104a3611a6b565b6104a3611bbe565b6104a3611d13565b6013546014546106fe919082565b600654610589906001600160a01b031681565b61048061088536600461394c565b600a60209081525f928352604080842090915290825290205460ff1681565b61045a6108b236600461394c565b611dcf565b6104ad611e92565b6104a36108cd366004613976565b611e9f565b60175460185461078c916001600160a01b03169082565b60105460115460125461092d926001600160401b0380821693600160401b8304821693600160801b8404831693600160c01b9004909216916001600160a01b031686565b604080516001600160401b0397881681529587166020870152938616938501939093529316606083015260808201929092526001600160a01b0390911660a082015260c001610464565b61045a6109853660046138f2565b611ea9565b6009546105899061010090046001600160a01b031681565b6104806109b0366004613924565b611eb4565b61045a6109c33660046138f2565b611f17565b6104a36109d6366004613976565b611f23565b61045a6109e93660046138f2565b611fe2565b61045a6109fc366004613b47565b612050565b610480610a0f3660046139c8565b600b60209081525f928352604080842090915290825290205460ff1681565b61045a610a3c3660046138f2565b6121a4565b61045a610a4f366004613b47565b6121af565b61045a610a62366004613976565b6122e1565b61045a610a753660046138f2565b61232a565b610480610a88366004613924565b600c60209081525f928352604080842090915290825290205460ff1681565b61045a610ab5366004613c0c565b612349565b61045a610ac8366004613976565b6001600160a01b03165f908152600d602052604090206003015490565b6104a3610af3366004613cbe565b6124e5565b61045a610b06366004613976565b6001600160a01b03165f908152600d602052604090206002015490565b61045a610b313660046138f2565b612723565b610bce6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a082015290565b6040516104649190613d7d565b6104a3610be9366004613da1565b61272f565b61045a610bfc3660046139c8565b600460209081525f928352604080842090915290825290205481565b61045a7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6104a3610c4d36600461388c565b612750565b61045a610c6036600461394c565b6001600160a01b03165f908152600d6020526040902060020154919050565b61045a610c8d3660046138f2565b6127e0565b61045a610ca036600461394c565b6001600160a01b03165f908152600d6020526040902054919050565b601654600254600954604051632b9a19db60e21b8152600481019290925261010090046001600160a01b0390811660248301527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811660448301525f92169063ae68676c90606401602060405180830381865afa158015610d3f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d639190613e36565b905090565b5f6001600160e01b03198216631883ba3960e21b1480610d8c5750610d8c8261284e565b92915050565b610d9a61289e565b610da3816128ea565b50565b5f8054610db290613e4d565b80601f0160208091040260200160405190810160405280929190818152602001828054610dde90613e4d565b8015610e295780601f10610e0057610100808354040283529160200191610e29565b820191905f5260205f20905b815481529060010190602001808311610e0c57829003601f168201915b505050505081565b6002545f908015610e5457610e4f610e47610cbc565b8490836129b9565b610e56565b825b9392505050565b335f8181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610eb79086815260200190565b60405180910390a350600192915050565b60405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6173796e632d666c6f7700000000000000000060448201525f906064015b60405180910390fd5b5f610f206129d4565b610f298361110d565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b82048116938301849052600160c01b90910416606082015260115460808201526012546001600160a01b031660a08201529192505f90610fa0908490670de0b6b3a76400006129b9565b9050610fac3086612ae8565b610fc0610fb98285613e99565b8686612b4f565b50610fcf818360a00151612c6e565b505092915050565b610fdf61289e565b600780546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6001600160a01b0383165f9081526004602090815260408083203384529091528120545f19811461108c576110688382613e99565b6001600160a01b0386165f9081526004602090815260408083203384529091529020555b6001600160a01b0385165f90815260036020526040812080548592906110b3908490613e99565b90915550506001600160a01b038085165f81815260036020526040908190208054870190555190918716905f805160206141da833981519152906110fa9087815260200190565b60405180910390a3506001949350505050565b600254600f545f919082906111469061112e90670de0b6b3a7640000613e99565b670de0b6b3a764000061113f610cbc565b91906129b9565b9050811561115e576111598482846129b9565b611160565b835b949350505050565b61117061289e565b6001600160a01b0381166111bf5760405162461bcd60e51b8152602060048201526016602482015275536166655661756c742f696e76616c69642d7361666560501b6044820152606401610f0e565b6040805180820182526001600160a01b038316808252426020909201829052601780546001600160a01b0319168217905560189190915590517f500119a4be3e95be870ef423be9332d25076b52ba5ca53b00f8fa823b61f6e2b905f90a250565b61122a8282612cad565b5050565b5f610d8c823333612050565b61124261289e565b5f838152600a602090815260408083206001600160a01b03861680855290835292819020805460ff19168515159081179091558151878152928301939093528101919091527f5f0ecfd1ea5555d5b4b6140b49c92365beaf40d0a057dc34a9746990cd4ce8d49060600160405180910390a1505050565b5f7f000000000000000000000000000000000000000000000000000000000000000146146112e957610d63612dc5565b507f7845ffda3f68688c409e25da9fad0150eea1fc5327dedcd42f09b90ffe8ba20290565b61131661289e565b61131e612e5d565b565b5f8061132a610cbc565b6013546009549192509060ff161561134557505f9392505050565b5f198103611354579392505050565b80821015611366576111598282613e99565b5f949350505050565b5f6001600160a01b03831633036113985760405162461bcd60e51b8152600401610f0e90613eac565b335f818152600b602090815260408083206001600160a01b03881680855290835292819020805460ff191687151590811790915590519081529192917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa2679101610eb7565b611404612ea5565b61131e6129d4565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a08201525f9061147781612eea565b91505090565b5f611486612ea5565b6114908382612f07565b611499836127e0565b9050805f036114ba5760405162461bcd60e51b8152600401610f0e90613ef4565b6114e67f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2333086612f0f565b6114f08282612fa7565b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610d8c8382612ff6565b5f856001600160a01b0316876001600160a01b0316036115725760405162461bcd60e51b8152600401610f0e90613eac565b824211156115b95760405162461bcd60e51b8152602060048201526014602482015273115490cdcd4d0c15985d5b1d0bd95e1c1a5c995960621b6044820152606401610f0e565b6001600160a01b0387165f908152600c6020908152604080832087845290915290205460ff161561162c5760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f617574686f72697a6174696f6e2d75736564006044820152606401610f0e565b6001600160a01b0387165f908152600c602090815260408083208784528252808320805460ff191660011790559084015190840151606085015191929091901a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156116dc5760405162461bcd60e51b815260206004820181905260248201527f455243373534305661756c742f696e76616c69642d7369676e61747572652d736044820152606401610f0e565b5f60016116e76112b9565b7fa3efcf8cb518126a85cdfd1c1102ee539e0700189f80926e1ac37144450473fa8d8d8d8d8d604051602001611753969594939291909586526001600160a01b03948516602087015292909316604085015215156060840152608083019190915260a082015260c00190565b6040516020818303038152906040528051906020012060405160200161179092919061190160f01b81526002810192909252602282015260420190565b60408051601f1981840301815282825280516020918201205f84529083018083525260ff851690820152606081018690526080810185905260a0016020604051602081039080840390855afa1580156117eb573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381161580159061182157508a6001600160a01b0316816001600160a01b0316145b61185e5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610f0e565b6001600160a01b038b81165f818152600b60209081526040808320948f1680845294825291829020805460ff19168e151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a35060019a9950505050505050505050565b6007546001600160a01b031633146119265760405162461bcd60e51b815260206004820152601360248201527213dddb99590bdb9bdd0b5b9bdb5a5b985d1959606a1b6044820152606401610f0e565b600654600754604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160078054600680546001600160a01b03199081166001600160a01b03841617909155169055565b6014545f908410156119ec5760405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6d696e2d616d6f756e740000000000000000006044820152606401610f0e565b611160848484613036565b611a1e60405180608001604052805f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600d6020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b611a7361289e565b604080518082019091526017546001600160a01b03168082526018546020830152611ae05760405162461bcd60e51b815260206004820152601a60248201527f536166655661756c742f6e6f2d736166652d70726f706f7365640000000000006044820152606401610f0e565b4281602001516203f480611af49190613f19565b1115611b4c5760405162461bcd60e51b815260206004820152602160248201527f536166655661756c742f736166652d6e6f742d7965742d61636365707461626c6044820152606560f81b6064820152608401610f0e565b80516015546040516001600160a01b0392831692909116907f260a9a29aa1c98abc86285a91c26a04cce10cb086152a137bf2f444ca24a45ca905f90a38051601580546001600160a01b039092166001600160a01b03199283161790556017805490911690555f601855610da3613237565b611bc661289e565b604080518082019091526019546001600160a01b0316808252601a546020830152611c335760405162461bcd60e51b815260206004820152601c60248201527f536166655661756c742f6e6f2d6f7261636c652d70726f706f736564000000006044820152606401610f0e565b4281602001516203f480611c479190613f19565b1115611ca15760405162461bcd60e51b815260206004820152602360248201527f536166655661756c742f6f7261636c652d6e6f742d7965742d61636365707461604482015262626c6560e81b6064820152608401610f0e565b80516016546040516001600160a01b0392831692909116907f05cd89403c6bdeac21c2ff33de395121a31fa1bc2bf3adf4825f1f86e79969dd905f90a38051601680546001600160a01b039092166001600160a01b03199283161790556019805490911690555f601a55610da3613237565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff1680611d7b57506006546001600160a01b031633145b611dc75760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a65640000000000006044820152606401610f0e565b610da3613237565b5f611dd8612ea5565b611de28184612f07565b825f03611e015760405162461bcd60e51b8152600401610f0e90613ef4565b611e0a83611fe2565b9050611e387f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2333084612f0f565b611e428284612fa7565b60408051828152602081018590526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610d8c8184612ff6565b60018054610db290613e4d565b610da38133612cad565b5f610d8c8233611dcf565b335f90815260036020526040812080548391908390611ed4908490613e99565b90915550506001600160a01b0383165f81815260036020526040908190208054850190555133905f805160206141da83398151915290610eb79086815260200190565b5f610d8c823333611998565b611f2b61289e565b6001600160a01b038116611f815760405162461bcd60e51b815260206004820152601860248201527f536166655661756c742f696e76616c69642d6f7261636c6500000000000000006044820152606401610f0e565b6040805180820182526001600160a01b038316808252426020909201829052601980546001600160a01b03191682179055601a9190915590517f0138b9794085eddff5afe429af2255342c44615c1c1f81ce93a1e38a476cee58905f90a250565b60408051808201909152601354815260145460208201525f908161200584610e31565b60095490915060ff168061202b575081518161201f610cbc565b6120299190613f19565b115b806120395750816020015184105b1561204757505f9392505050565b6111608461327c565b5f6001600160a01b03821633148061208a57506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6120a65760405162461bcd60e51b8152600401610f0e90613f2c565b835f036120e35760405162461bcd60e51b815260206004820152600b60248201526a5a45524f5f41535345545360a81b6044820152606401610f0e565b6001600160a01b0382165f908152600d602052604090206002810154600382015461210f91879161329d565b915061211b85826132c0565b6121258583613326565b6121507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28587613338565b60408051868152602081018490526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a45b509392505050565b5f610d8c823361147d565b5f6001600160a01b0382163314806121e957506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6122055760405162461bcd60e51b8152600401610f0e90613f2c565b835f036122245760405162461bcd60e51b8152600401610f0e90613ef4565b6001600160a01b0382165f908152600d60205260409020600381015460028201546122509187916129b9565b915061225c85826133b5565b6122668286613326565b6122917f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28584613338565b60408051838152602081018790526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a461219c565b5f806122eb610cbc565b6013546009549192509060ff161561230657505f9392505050565b5f198103612315579392505050565b8082101561136657611159610a758383613e99565b6002545f908015610e5457610e4f81612341610cbc565b8591906129b9565b5f815183511461236c576040516321f9f13f60e11b815260040160405180910390fd5b6123746129d4565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a08201525f80805b86518110156124c3575f6124048883815181106123f7576123f7613f63565b602002602001015161110d565b90505f61243186604001516001600160401b0316670de0b6b3a7640000846129b99092919063ffffffff16565b90506124796124408284613e99565b8a858151811061245257612452613f63565b60200260200101518a868151811061246c5761246c613f63565b6020026020010151612b4f565b506124848288613f19565b96506124908185613f19565b93508883815181106124a4576124a4613f63565b6020026020010151856124b79190613f19565b945050506001016123d8565b506124ce3083612ae8565b6124dc818460a00151612c6e565b50505092915050565b428410156125355760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610f0e565b5f60016125406112b9565b6001600160a01b038a81165f8181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612648573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381161580159061267e5750876001600160a01b0316816001600160a01b0316145b6126bb5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610f0e565b6001600160a01b039081165f9081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b5f610d8c8233336121af565b61273761289e565b61273f612ea5565b6127476129d4565b610da38161341b565b61275861289e565b6702c68af0bb1400008160200151118061277a575080516702c68af0bb140000105b15612798576040516321f9f13f60e11b815260040160405180910390fd5b7f773cd47b12eabb5832b83bb94bc847b3e000c5b824fdf9da506cb9ecf9e1a53f600e826040516127ca929190613f77565b60405180910390a18051600e5560200151600f55565b60408051808201909152601354815260145460208201525f90816128038461232a565b60095490915060ff1680612829575081518461281d610cbc565b6128279190613f19565b115b806128375750816020015181105b1561284557505f9392505050565b611160846135f7565b5f6001600160e01b03198216632f0a18c560e01b148061287e57506001600160e01b0319821663e3bc4e6560e01b145b80610d8c57506001600160e01b031982166301ffc9a760e01b1492915050565b6006546001600160a01b0316331461131e5760405162461bcd60e51b815260206004820152600f60248201526e27bbb732b217b737ba16b7bbb732b960891b6044820152606401610f0e565b600254801580159061290357506128ff610cbc565b8251105b15612921576040516321f9f13f60e11b815260040160405180910390fd5b61294c7f0000000000000000000000000000000000000000000000000000000000000012600a61407f565b82602001511115612970576040516321f9f13f60e11b815260040160405180910390fd5b7ff455e80a7d5b3f148849bb4acc1e909fafb067275e80e335f966127410c9169f6013836040516129a2929190613f77565b60405180910390a150805160135560200151601455565b5f825f1904841183021582026129cd575f80fd5b5091020490565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a08201525f612a3e8261360c565b90505f612a4a836136c4565b90505f612a7b6104c87f0000000000000000000000000000000000000000000000000000000000000012600a61407f565b90505f612a888385613f19565b1115612aa95760a0840151612aa990612aa4610a758587613f19565b612fa7565b8360800151811115612abb5760118190555b8115612ae257601080546001600160c01b0316600160c01b426001600160401b0316021790555b50505050565b6001600160a01b0382165f9081526003602052604081208054839290612b0f908490613e99565b90915550506002805482900390556040518181525f906001600160a01b038416905f805160206141da833981519152906020015b60405180910390a35050565b5f831580612b5b575082155b15612b785760405162461bcd60e51b8152600401610f0e90613ef4565b6001600160a01b0382165f908152600d60205260409020805415801590612ba0575080548411155b612bbc5760405162461bcd60e51b8152600401610f0e90613ef4565b612bc6858561372c565b83816002015f828254612bd99190613f19565b9250508190555084816003015f828254612bf39190613f19565b90915550508054849082905f90612c0b908490613e99565b909155505080545f03612c1f575f60018201555b604080518581526020810187905233916001600160a01b038616917f24111f527e6debb0efcfd4c847fc0ae4d8858cdfc72cc2fce0e757a3fce414f7910160405180910390a350929392505050565b811561122a5760155461122a907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906001600160a01b03168385612f0f565b6001600160a01b038216331480612ce657506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b612d025760405162461bcd60e51b8152600401610f0e90613f2c565b6001600160a01b0382165f908152600d60205260409020805480612d685760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f6e6f2d70656e64696e672d72657175657374006044820152606401610f0e565b612d73308483613338565b5f80835560018301556040518181526001600160a01b0384811691908616907f756776540459f688c5059ee7547eed3bfaf8143dc14677d8fcef3007aceec9fc9060200160405180910390a350505050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f604051612df5919061408d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b612e65613765565b6009805460ff191690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60095460ff161561131e5760405162461bcd60e51b815260206004820152600f60248201526e14185d5cd8589b194bdc185d5cd959608a1b6044820152606401610f0e565b5f612ef4826136c4565b612efd8361360c565b610d8c9190613f19565b61122a6129d4565b5f6040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f511416171691505080612fa05760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610f0e565b5050505050565b8060025f828254612fb89190613f19565b90915550506001600160a01b0382165f818152600360209081526040808320805486019055518481525f805160206141da8339815191529101612b43565b612ffe6129d4565b60155461122a907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906001600160a01b031684613338565b5f6001600160a01b03821633148061307057506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6130bc5760405162461bcd60e51b815260206004820152601a60248201527f455243373534305661756c742f696e76616c69642d6f776e65720000000000006044820152606401610f0e565b6040516370a0823160e01b81526001600160a01b0383166004820152849030906370a0823190602401602060405180830381865afa158015613100573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131249190613e36565b101561317c5760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f696e73756666696369656e742d62616c616e636044820152606560f81b6064820152608401610f0e565b835f0361319b5760405162461bcd60e51b8152600401610f0e90613ef4565b6131a730833087612f0f565b6001600160a01b0383165f908152600d602052604081208054909186918391906131d2908490613f19565b9091555050426001820155604080515f81523360208201529081018690526001600160a01b0384811691908616907f1e7ddf69e1e9242eb5635deb06ffd4691a8962ec488a863a26721288c88985519060600160405180910390a3505f949350505050565b61323f612ea5565b6009805460ff191660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602001612e9b565b6009545f9060ff1661329657613291826137ad565b610d8c565b5f92915050565b5f825f1904841183021582026132b1575f80fd5b50910281810615159190040190565b5f6132de826002015483600301548561329d9092919063ffffffff16565b905082826003015f8282546132f39190613e99565b909155505060028201548110613309575f613319565b8082600201546133199190613e99565b8260020181905550505050565b60095460ff1661122a5761122a6129d4565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080612ae25760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610f0e565b5f6133d3826003015483600201548561329d9092919063ffffffff16565b9050808260030154116133e6575f6133f6565b8082600301546133f69190613e99565b826003018190555082826002015f8282546134119190613e99565b9091555050505050565b6702c68af0bb140000815f01516001600160401b03161180613450575066b1a2bc2ec5000081602001516001600160401b0316115b8061346e575066b1a2bc2ec5000081604001516001600160401b0316115b1561348c576040516321f9f13f60e11b815260040160405180910390fd5b60a08101516001600160a01b03166134b7576040516321f9f13f60e11b815260040160405180910390fd5b6001600160401b03421660608201526011545f03613507576134fd6104c87f0000000000000000000000000000000000000000000000000000000000000012600a61407f565b6080820152613510565b60115460808201525b7faf0d4a9793f0cb3dd1b01241953e01dc6eb6515b9e5cd623f1b3cad0fc1fa45660108260405161354292919061412b565b60405180910390a18051601080546020840151604085015160608601516001600160401b03908116600160c01b026001600160c01b03928216600160801b02929092166fffffffffffffffffffffffffffffffff938216600160401b026fffffffffffffffffffffffffffffffff199095169190961617929092171692909217919091179055608081015160115560a00151601280546001600160a01b039092166001600160a01b0319909216919091179055565b6009545f9060ff1661329657613291826137cb565b5f8061363c6104c87f0000000000000000000000000000000000000000000000000000000000000012600a61407f565b83519091506001600160401b0316801580159061365c5750836080015182115b613666575f611160565b61116060025485608001518461367c9190613e99565b613686919061418a565b6136b17f000000000000000000000000000000000000000000000000000000000000001260126141a1565b6136bc90600a61407f565b83919061329d565b60208101515f906001600160401b0316806136df575f610e56565b670de0b6b3a764000061372284606001516001600160401b0316426137049190613e99565b61370c610cbc565b613716919061418a565b83906301e133806129b9565b610e5691906141ba565b60155461122a907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906001600160a01b03163085612f0f565b60095460ff1661131e5760405162461bcd60e51b815260206004820152601360248201527214185d5cd8589b194bdb9bdd0b5c185d5cd959606a1b6044820152606401610f0e565b6002545f908015610e5457610e4f6137c3610cbc565b84908361329d565b5f610d8c8261232a565b5f602082840312156137e5575f80fd5b81356001600160e01b031981168114610e56575f80fd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613838576138386137fc565b604052919050565b5f60408284031215613850575f80fd5b604051604081018181106001600160401b0382111715613872576138726137fc565b604052823581526020928301359281019290925250919050565b5f6040828403121561389c575f80fd5b610e568383613840565b5f602080835283518060208501525f5b818110156138d2578581018301518582016040015282016138b6565b505f604082860101526040601f19601f8301168501019250505092915050565b5f60208284031215613902575f80fd5b5035919050565b80356001600160a01b038116811461391f575f80fd5b919050565b5f8060408385031215613935575f80fd5b61393e83613909565b946020939093013593505050565b5f806040838503121561395d575f80fd5b8235915061396d60208401613909565b90509250929050565b5f60208284031215613986575f80fd5b610e5682613909565b5f805f606084860312156139a1575f80fd5b6139aa84613909565b92506139b860208501613909565b9150604084013590509250925092565b5f80604083850312156139d9575f80fd5b6139e283613909565b915061396d60208401613909565b8035801515811461391f575f80fd5b5f805f60608486031215613a11575f80fd5b83359250613a2160208501613909565b9150613a2f604085016139f0565b90509250925092565b815181526020808301519082015260408101610d8c565b5f8060408385031215613a60575f80fd5b613a6983613909565b915061396d602084016139f0565b5f805f805f8060c08789031215613a8c575f80fd5b613a9587613909565b95506020613aa4818901613909565b9550613ab2604089016139f0565b9450606088013593506080880135925060a08801356001600160401b0380821115613adb575f80fd5b818a0191508a601f830112613aee575f80fd5b813581811115613b0057613b006137fc565b613b12601f8201601f19168501613810565b91508082528b84828501011115613b27575f80fd5b80848401858401375f848284010152508093505050509295509295509295565b5f805f60608486031215613b59575f80fd5b83359250613b6960208501613909565b9150613a2f60408501613909565b5f6001600160401b03821115613b8f57613b8f6137fc565b5060051b60200190565b5f82601f830112613ba8575f80fd5b81356020613bbd613bb883613b77565b613810565b8083825260208201915060208460051b870101935086841115613bde575f80fd5b602086015b84811015613c0157613bf481613909565b8352918301918301613be3565b509695505050505050565b5f8060408385031215613c1d575f80fd5b82356001600160401b0380821115613c33575f80fd5b818501915085601f830112613c46575f80fd5b81356020613c56613bb883613b77565b82815260059290921b84018101918181019089841115613c74575f80fd5b948201945b83861015613c9257853582529482019490820190613c79565b96505086013592505080821115613ca7575f80fd5b50613cb485828601613b99565b9150509250929050565b5f805f805f805f60e0888a031215613cd4575f80fd5b613cdd88613909565b9650613ceb60208901613909565b95506040880135945060608801359350608088013560ff81168114613d0e575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b80516001600160401b039081168352602080830151821690840152604080830151821690840152606080830151909116908301526080808201519083015260a0908101516001600160a01b0316910152565b60c08101610d8c8284613d2b565b80356001600160401b038116811461391f575f80fd5b5f60c08284031215613db1575f80fd5b60405160c081018181106001600160401b0382111715613dd357613dd36137fc565b604052613ddf83613d8b565b8152613ded60208401613d8b565b6020820152613dfe60408401613d8b565b6040820152613e0f60608401613d8b565b606082015260808301356080820152613e2a60a08401613909565b60a08201529392505050565b5f60208284031215613e46575f80fd5b5051919050565b600181811c90821680613e6157607f821691505b602082108103613e7f57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d8c57610d8c613e85565b60208082526028908201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60408201526737b832b930ba37b960c11b606082015260800190565b6020808252600b908201526a5a45524f5f53484152455360a81b604082015260600190565b80820180821115610d8c57610d8c613e85565b6020808252601b908201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b8254815260018301546020820152608081018251604083015260208301516060830152610e56565b600181815b80851115613fd957815f1904821115613fbf57613fbf613e85565b80851615613fcc57918102915b93841c9390800290613fa4565b509250929050565b5f82613fef57506001610d8c565b81613ffb57505f610d8c565b8160018114614011576002811461401b57614037565b6001915050610d8c565b60ff84111561402c5761402c613e85565b50506001821b610d8c565b5060208310610133831016604e8410600b841016171561405a575081810a610d8c565b6140648383613f9f565b805f190482111561407757614077613e85565b029392505050565b5f610e5660ff841683613fe1565b5f8083545f60018260011c915060018316806140aa57607f831692505b602080841082036140c957634e487b7160e01b5f52602260045260245ffd5b8180156140dd57600181146140f25761411d565b60ff198616895284151585028901965061411d565b5f8a8152602090205f5b868110156141155781548b8201529085019083016140fc565b505084890196505b509498975050505050505050565b82546001600160401b038082168352604082811c82166020850152608083811c9092169084015260c091821c606084015260018501549083015260028401546001600160a01b031660a0830152610180820190610e5690830184613d2b565b8082028115828204841417610d8c57610d8c613e85565b60ff8181168382160190811115610d8c57610d8c613e85565b5f826141d457634e487b7160e01b5f52601260045260245ffd5b50049056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212201a6b3e2cae11e90e50d00e014a255fa4b4d812fc97b6b5aa1f622b778ffaf83a64736f6c63430008190033

Verified Source Code Full Match

Compiler: v0.8.25+commit.b61c2a91 EVM: shanghai Optimization: Yes (200 runs)
OracleVault.sol 189 lines
// SPDX-License-Identifier: GPL-3.0
// Docgen-SOLC: 0.8.25

pragma solidity ^0.8.25;

import {AsyncVault, InitializeParams} from "./AsyncVault.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {IPriceOracle} from "src/interfaces/IPriceOracle.sol";

struct ChangeProposal {
    address addr;
    uint256 timestamp;
}

/**
 * @title   OracleVault
 * @author  RedVeil
 * @notice  ERC-7540 (https://eips.ethereum.org/EIPS/eip-7540) compliant async redeem vault using a PushOracle for pricing and a Safe for managing assets
 * @dev     Oracle and safe security is handled in other contracts. We simply assume they are secure and don't implement any further checks in this contract
 */
contract OracleVault is AsyncVault {
    address public safe;

    /**
     * @notice Constructor for the OracleVault
     * @param params The parameters to initialize the vault with
     * @param oracle_ The oracle to use for pricing
     * @param safe_ The safe which will manage the assets
     */
    constructor(
        InitializeParams memory params,
        address oracle_,
        address safe_
    ) AsyncVault(params) {
        if (safe_ == address(0) || oracle_ == address(0))
            revert Misconfigured();

        safe = safe_;
        oracle = IPriceOracle(oracle_);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    IPriceOracle public oracle;

    /// @notice Total amount of underlying `asset` token managed by the safe.
    function totalAssets() public view override returns (uint256) {
        return oracle.getQuote(totalSupply, share, address(asset));
    }

    /*//////////////////////////////////////////////////////////////
                            ERC-4626 OVERRIDES
    //////////////////////////////////////////////////////////////*/

    /// @dev Internal function to handle the deposit and mint
    function afterDeposit(uint256 assets, uint256) internal override {
        // Deposit and mint already have the `whenNotPaused` modifier so we don't need to check it here
        _takeFees();

        // Transfer assets to the safe
        SafeTransferLib.safeTransfer(asset, safe, assets);
    }

    /*//////////////////////////////////////////////////////////////
                    BaseControlledAsyncRedeem OVERRIDES
    //////////////////////////////////////////////////////////////*/

    /// @dev Internal function to transfer assets from the safe to the vault before fulfilling a redeem
    function beforeFulfillRedeem(uint256 assets, uint256) internal override {
        SafeTransferLib.safeTransferFrom(asset, safe, address(this), assets);
    }

    /*//////////////////////////////////////////////////////////////
                    AsyncVault OVERRIDES
    //////////////////////////////////////////////////////////////*/

    /// @dev Internal function to handle the withdrawal incentive
    function handleWithdrawalIncentive(
        uint256 fee,
        address feeRecipient
    ) internal override {
        if (fee > 0)
            // Transfer the fee from the safe to the fee recipient
            SafeTransferLib.safeTransferFrom(asset, safe, feeRecipient, fee);
    }

    /*//////////////////////////////////////////////////////////////
                        SWITCH SAFE LOGIC
    //////////////////////////////////////////////////////////////*/

    ChangeProposal public proposedSafe;

    event SafeProposed(address indexed proposedSafe);
    event SafeChanged(address indexed oldSafe, address indexed newSafe);

    /**
     * @notice Proposes a new safe that can be accepted by the owner after a delay
     * @param newSafe The new safe to propose
     * @dev !!! This is a dangerous operation and should be used with extreme caution !!!
     */
    function proposeSafe(address newSafe) external onlyOwner {
        require(newSafe != address(0), "SafeVault/invalid-safe");

        proposedSafe = ChangeProposal({
            addr: newSafe,
            timestamp: block.timestamp
        });

        emit SafeProposed(newSafe);
    }

    /**
     * @notice Accepts the proposed safe
     * @dev !!! This is a dangerous operation and should be used with extreme caution !!!
     * @dev This will pause the vault to ensure the oracle is set up correctly and no one sends deposits with faulty prices
     * @dev Its important to ensure that the oracle will be switched before unpausing the vault again
     */
    function acceptSafe() external onlyOwner {
        ChangeProposal memory proposal = proposedSafe;

        require(proposal.addr != address(0), "SafeVault/no-safe-proposed");
        require(
            proposal.timestamp + 3 days <= block.timestamp,
            "SafeVault/safe-not-yet-acceptable"
        );

        emit SafeChanged(safe, proposal.addr);

        safe = proposal.addr;

        delete proposedSafe;

        // Pause to ensure that no deposits get through with faulty prices
        _pause();
    }

    /*//////////////////////////////////////////////////////////////
                        SWITCH ORACLE LOGIC
    //////////////////////////////////////////////////////////////*/

    ChangeProposal public proposedOracle;

    event OracleProposed(address indexed proposedOracle);
    event OracleChanged(address indexed oldOracle, address indexed newOracle);

    /**
     * @notice Proposes a new oracle that can be accepted by the owner after a delay
     * @param newOracle The new oracle to propose
     * @dev !!! This is a dangerous operation and should be used with extreme caution !!!
     */
    function proposeOracle(address newOracle) external onlyOwner {
        require(newOracle != address(0), "SafeVault/invalid-oracle");

        proposedOracle = ChangeProposal({
            addr: newOracle,
            timestamp: block.timestamp
        });

        emit OracleProposed(newOracle);
    }

    /**
     * @notice Accepts the proposed oracle
     * @dev !!! This is a dangerous operation and should be used with extreme caution !!!
     * @dev This will pause the vault to ensure the oracle is set up correctly and no one sends deposits with faulty prices
     * @dev Its important to ensure that the oracle will be switched before unpausing the vault again
     */
    function acceptOracle() external onlyOwner {
        ChangeProposal memory proposal = proposedOracle;

        require(proposal.addr != address(0), "SafeVault/no-oracle-proposed");
        require(
            proposal.timestamp + 3 days <= block.timestamp,
            "SafeVault/oracle-not-yet-acceptable"
        );

        emit OracleChanged(address(oracle), proposal.addr);

        oracle = IPriceOracle(proposal.addr);

        delete proposedOracle;

        // Pause to ensure that no deposits get through with faulty prices
        _pause();
    }
}
AsyncVault.sol 565 lines
// SPDX-License-Identifier: GPL-3.0
// Docgen-SOLC: 0.8.25

pragma solidity ^0.8.25;

import {BaseControlledAsyncRedeem} from "./BaseControlledAsyncRedeem.sol";
import {BaseERC7540, ERC20} from "./BaseERC7540.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";

/// @notice Handles the initialize parameters of the vault
struct InitializeParams {
    /// @notice The address of the asset that the vault will manage
    address asset;
    /// @notice The name of the vault
    string name;
    /// @notice The symbol of the vault
    string symbol;
    /// @notice The trusted manager of the vault (handles all sensitive management logic)
    address owner;
    /// @notice The limits of the vault
    Limits limits;
    /// @notice The fees of the vault
    Fees fees;
}

/// @notice Stores the bounds of the vault
struct Bounds {
    /// @notice Upper bound of the vault (will be used for future profit calculations of the manager)
    uint256 upper;
    /// @notice Lower bound of the vault (used on withdrawals to ensure an additional asset buffer between the reported totalAssets and the actual totalAssets)
    uint256 lower;
}

/// @notice Stores the deposit limit and minAmounts of the vault
struct Limits {
    /// @notice Maximum amount of assets that can be deposited into the vault
    uint256 depositLimit;
    /// @notice Minimum amount of shares that can be minted / redeemed from the vault
    uint256 minAmount;
}

/// @notice Stores all fee related variables
struct Fees {
    /// @notice Performance fee rate in 1e18 (100% = 1e18)
    uint64 performanceFee;
    /// @notice Management fee rate in 1e18 (100% = 1e18)
    uint64 managementFee;
    /// @notice Withdrawal incentive fee rate in 1e18 (100% = 1e18)
    uint64 withdrawalIncentive;
    /// @notice Timestamp of the last time the fees were updated (used for management fee calculations)
    uint64 feesUpdatedAt;
    /// @notice High water mark of the vault (used for performance fee calculations)
    uint256 highWaterMark;
    /// @notice Address of the fee recipient
    address feeRecipient;
}

/**
 * @title   AsyncVault
 * @author  RedVeil
 * @notice  Abstract contract containing reusable logic that are the basis of ERC-7540 compliant async redeem vauls
 * @notice  Besides the basic logic for ERC-7540 this contract contains most other logic to manage a modern DeFi vault
 * @dev     Logic to account and manage assets must be implemented by inheriting contracts
 */
abstract contract AsyncVault is BaseControlledAsyncRedeem {
    using FixedPointMathLib for uint256;

    error ZeroAmount();
    error Misconfigured();

    /**
     * @notice Constructor for AsyncVault
     * @param params The initialization parameters
     */
    constructor(
        InitializeParams memory params
    ) BaseERC7540(params.owner, params.asset, params.name, params.symbol) {
        _setLimits(params.limits);
        _setFees(params.fees);
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Deposit assets into the vault
     * @param assets The amount of assets to deposit
     * @return shares The amount of shares required
     */
    function deposit(uint256 assets) external returns (uint256) {
        return deposit(assets, msg.sender);
    }

    /**
     * @notice Mint shares into the vault
     * @param shares The amount of shares to mint
     * @return assets The amount of assets received
     */
    function mint(uint256 shares) external returns (uint256) {
        return mint(shares, msg.sender);
    }

    /**
     * @notice Withdraw assets from the vault
     * @param assets The amount of assets to withdraw
     * @return shares The amount of shares required
     */
    function withdraw(uint256 assets) external returns (uint256) {
        return withdraw(assets, msg.sender, msg.sender);
    }

    /**
     * @notice Redeem shares from the vault
     * @param shares The amount of shares to redeem
     * @return assets The amount of assets received
     */
    function redeem(uint256 shares) external returns (uint256) {
        return redeem(shares, msg.sender, msg.sender);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Simulates a deposit into the vault and returns the amount of shares that would be received by the user
     * @param assets The amount of assets to deposit
     * @return shares The amount of shares that would be received by the user
     * @dev This function will return 0 if the vault is paused or if the deposit doesnt meet the limits
     */
    function previewDeposit(
        uint256 assets
    ) public view override returns (uint256) {
        Limits memory limits_ = limits;
        uint256 shares = convertToShares(assets);

        if (
            paused ||
            totalAssets() + assets > limits_.depositLimit ||
            shares < limits_.minAmount
        ) return 0;

        return super.previewDeposit(assets);
    }

    /**
     * @notice Simulates a mint into the vault and returns the amount of assets required to mint the given amount of shares
     * @param shares The amount of shares to mint
     * @return assets The amount of assets required to mint the given amount of shares
     * @dev This function will return 0 if the vault is paused or if the mint doesnt meet the limits
     */
    function previewMint(
        uint256 shares
    ) public view override returns (uint256) {
        Limits memory limits_ = limits;
        uint256 assets = convertToAssets(shares);

        if (
            paused ||
            totalAssets() + assets > limits_.depositLimit ||
            shares < limits_.minAmount
        ) return 0;

        return super.previewMint(shares);
    }

    /**
     * @notice Converts shares to assets based on a lower bound of totalAssets
     * @param shares The amount of shares to convert
     * @return lowerTotalAssets The lower bound value of assets that correspond to the given amount of shares
     * @dev This function is used on redeem fulfillment to ensure an additional asset buffer between the reported totalAssets and the actual totalAssets.
     * In most cases this will be the same as `convertToAssets` but same vaults might need to add a small buffer if they use volatile strategies or assets that are hard to sell.
     */
    function convertToLowBoundAssets(
        uint256 shares
    ) public view returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
        uint256 assets = totalAssets().mulDivDown(1e18 - bounds.lower, 1e18);

        return supply == 0 ? shares : shares.mulDivDown(assets, supply);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the maximum amount of assets that can be deposited into the vault
     * @return assetsThe maxDeposit of the controller
     * @dev Will return 0 if the vault is paused or if the deposit limit is reached
     */
    function maxDeposit(address) public view override returns (uint256) {
        uint256 assets = totalAssets();
        uint256 depositLimit_ = limits.depositLimit;

        if (paused) return 0;
        if (depositLimit_ == type(uint256).max) return depositLimit_;

        return assets >= depositLimit_ ? 0 : depositLimit_ - assets;
    }
    /**
     * @notice Returns the maximum amount of shares that can be minted into the vault
     * @return shares The maxMint of the controller
     * @dev Will return 0 if the vault is paused or if the deposit limit is reached
     * @dev Overflows if depositLimit is close to maxUint (convertToShares multiplies depositLimit with totalSupply)
     */
    function maxMint(address) public view override returns (uint256) {
        uint256 assets = totalAssets();
        uint256 depositLimit_ = limits.depositLimit;

        if (paused) return 0;
        if (depositLimit_ == type(uint256).max) return depositLimit_;

        return
            assets >= depositLimit_
                ? 0
                : convertToShares(depositLimit_ - assets);
    }

    /*//////////////////////////////////////////////////////////////
                        REQUEST REDEEM LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Requests a redeem for the caller
     * @param shares The amount of shares to redeem
     * @return requestId The requestId of the redeem request
     */
    function requestRedeem(uint256 shares) external returns (uint256) {
        return requestRedeem(shares, msg.sender, msg.sender);
    }

    /**
     * @notice Requests a redeem of shares from the vault
     * @param shares The amount of shares to redeem
     * @param controller The user that will be receiving pending shares
     * @param owner The owner of the shares to redeem
     * @return requestId The requestId of the redeem request
     * @dev This redeem request is added to any pending redeem request of the controller
     * @dev This function will revert if the shares are less than the minAmount
     */
    function requestRedeem(
        uint256 shares,
        address controller,
        address owner
    ) public override returns (uint256 requestId) {
        require(shares >= limits.minAmount, "ERC7540Vault/min-amount");

        return _requestRedeem(shares, controller, owner);
    }

    /*//////////////////////////////////////////////////////////////
                        FULFILL REDEEM LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Fulfills a redeem request of the controller to allow the controller to withdraw their assets
     * @param shares The amount of shares to redeem
     * @param controller The controller to redeem for
     * @return assets The amount of assets received
     * @dev This function will revert if the shares are less than the minAmount
     * @dev This function will also take the withdrawal incentive fee from the assets to incentivse the manager to fulfill the request
     */
    function fulfillRedeem(
        uint256 shares,
        address controller
    ) external override returns (uint256 assets) {
        // Take fees before fulfilling the redeem
        _takeFees();

        // Using the lower bound totalAssets ensures that even with volatile strategies and market conditions we will have sufficient assets to cover the redeem
        assets = convertToLowBoundAssets(shares);

        // Calculate the withdrawal incentive fee from the assets
        Fees memory fees_ = fees;
        uint256 fees = assets.mulDivDown(
            uint256(fees_.withdrawalIncentive),
            1e18
        );

        // Burn controller's shares
        _burn(address(this), shares);

        // Fulfill the redeem request
        _fulfillRedeem(assets - fees, shares, controller);

        // Send the withdrawal incentive fee to the fee recipient
        handleWithdrawalIncentive(fees, fees_.feeRecipient);
    }

    /**
     * @notice Fulfills multiple redeem requests of the controller to allow the controller to withdraw their assets
     * @param shares The amount of shares to redeem
     * @param controllers The controllers to redeem for
     * @return total The total amount of assets received
     * @dev This function will revert if the shares and controllers arrays are not the same length
     * @dev This function will also take the withdrawal incentive fee from the assets to incentivse the manager to fulfill the requests
     */
    function fulfillMultipleRedeems(
        uint256[] memory shares,
        address[] memory controllers
    ) external returns (uint256 total) {
        if (shares.length != controllers.length) revert Misconfigured();

        // Take fees before fulfilling the redeem
        _takeFees();

        // cache the fees
        Fees memory fees_ = fees;

        uint256 totalShares;
        uint256 totalFees;
        for (uint256 i; i < shares.length; i++) {
            // Using the lower bound totalAssets ensures that even with volatile strategies and market conditions we will have sufficient assets to cover the redeem
            uint256 assets = convertToLowBoundAssets(shares[i]);

            // Calculate the withdrawal incentive fee from the assets
            uint256 fees = assets.mulDivDown(
                uint256(fees_.withdrawalIncentive),
                1e18
            );

            // Fulfill the redeem request
            _fulfillRedeem(assets - fees, shares[i], controllers[i]);

            // Add to the total assets and fees
            total += assets;
            totalFees += fees;
            totalShares += shares[i];
        }

        // Burn controller's shares
        _burn(address(this), totalShares);

        // Send the withdrawal incentive fee to the fee recipient
        handleWithdrawalIncentive(totalFees, fees_.feeRecipient);

        return total;
    }

    /**
     * @notice Handles the withdrawal incentive fee by sending it to the fee recipient
     * @param fee The amount of fee to send
     * @param feeRecipient The address to send the fee to
     * @dev This function is expected to be overriden in inheriting contracts
     */
    function handleWithdrawalIncentive(
        uint256 fee,
        address feeRecipient
    ) internal virtual {
        if (fee > 0) SafeTransferLib.safeTransfer(asset, feeRecipient, fee);
    }

    /*//////////////////////////////////////////////////////////////
                            ERC-4626 OVERRIDES
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Takes fees before a withdraw (if the contract is not paused)
     * @dev This function is expected to be overriden in inheriting contracts
     */
    function beforeWithdraw(uint256 assets, uint256) internal virtual override {
        if (!paused) _takeFees();
    }

    /**
     * @notice Takes fees before a deposit
     * @dev This function is expected to be overriden in inheriting contracts
     */
    function beforeDeposit(uint256 assets, uint256) internal virtual override {
        // deposit and mint already have the `whenNotPaused` modifier so we don't need to check it here
        _takeFees();
    }

    /*//////////////////////////////////////////////////////////////
                            BOUND LOGIC
    //////////////////////////////////////////////////////////////*/

    Bounds public bounds;

    event BoundsUpdated(Bounds prev, Bounds next);

    /// @notice Returns the bounds of the vault
    function getBounds() public view returns (Bounds memory) {
        return bounds;
    }

    /**
     * @notice Sets the bounds of the vault to ensure that even with volatile strategies and market conditions we will have sufficient assets to cover the redeem
     * @param bounds_ The bounds to set
     * @dev This function will revert if the bounds are greater than or equal to 1e18
     */
    function setBounds(Bounds memory bounds_) external onlyOwner {
        // Bounds shouldnt be larger than 20%
        if (bounds_.lower > 2e17 || bounds_.upper > 2e17)
            revert Misconfigured();

        emit BoundsUpdated(bounds, bounds_);

        bounds = bounds_;
    }

    /*//////////////////////////////////////////////////////////////
                            FEE LOGIC
    //////////////////////////////////////////////////////////////*/

    Fees public fees;

    event FeesUpdated(Fees prev, Fees next);

    error InvalidFee(uint256 fee);

    /// @notice Returns the fees parameters of the vault
    function getFees() public view returns (Fees memory) {
        return fees;
    }

    /// @notice Returns the accrued fees of the vault
    function accruedFees() public view returns (uint256) {
        Fees memory fees_ = fees;

        return _accruedFees(fees_);
    }

    /// @dev Internal function to calculate the accrued fees
    function _accruedFees(Fees memory fees_) internal view returns (uint256) {
        return _accruedPerformanceFee(fees_) + _accruedManagementFee(fees_);
    }

    /**
     * @notice Performance fee that has accrued since last fee harvest.
     * @return accruedPerformanceFee In underlying `asset` token.
     * @dev Performance fee is based on a high water mark value. If vault share value has increased above the
     *   HWM in a fee period, issue fee shares to the vault equal to the performance fee.
     */
    function _accruedPerformanceFee(
        Fees memory fees_
    ) internal view returns (uint256) {
        uint256 shareValue = convertToAssets(10 ** decimals);
        uint256 performanceFee = uint256(fees_.performanceFee);

        return
            performanceFee > 0 && shareValue > fees_.highWaterMark
                ? performanceFee.mulDivUp(
                    (shareValue - fees_.highWaterMark) * totalSupply,
                    (10 ** (18 + decimals))
                )
                : 0;
    }

    /**
     * @notice Management fee that has accrued since last fee harvest.
     * @return accruedManagementFee In underlying `asset` token.
     * @dev Management fee is annualized per minute, based on 525,600 minutes per year. Total assets are calculated using
     *  the average of their current value and the value at the previous fee harvest checkpoint. This method is similar to
     *  calculating a definite integral using the trapezoid rule.
     */
    function _accruedManagementFee(
        Fees memory fees_
    ) internal view returns (uint256) {
        uint256 managementFee = uint256(fees_.managementFee);

        return
            managementFee > 0
                ? managementFee.mulDivDown(
                    totalAssets() * (block.timestamp - fees_.feesUpdatedAt),
                    31536000 // seconds per year
                ) / 1e18
                : 0;
    }

    /**
     * @notice Sets the fees of the vault
     * @param fees_ The fees to set
     * @dev This function will revert if the fees are greater than 20% performanceFee, 5% managementFee, or 5% withdrawalIncentive
     * @dev This function will also take the fees before setting them to ensure the new fees rates arent applied to any pending fees
     */
    function setFees(Fees memory fees_) public onlyOwner whenNotPaused {
        _takeFees();

        _setFees(fees_);
    }

    /// @dev Internal function to set the fees
    function _setFees(Fees memory fees_) internal {
        // Dont take more than 20% performanceFee, 5% managementFee, 5% withdrawalIncentive
        if (
            fees_.performanceFee > 2e17 ||
            fees_.managementFee > 5e16 ||
            fees_.withdrawalIncentive > 5e16
        ) revert Misconfigured();
        if (fees_.feeRecipient == address(0)) revert Misconfigured();

        // Dont rely on user input here
        fees_.feesUpdatedAt = uint64(block.timestamp);

        // initialise or copy current HWM
        if (fees.highWaterMark == 0) {
            fees_.highWaterMark = convertToAssets(10 ** decimals); // from constructor
        } else {
            fees_.highWaterMark = fees.highWaterMark; // from setFees
        }

        emit FeesUpdated(fees, fees_);

        fees = fees_;
    }

    /**
     * @notice Mints fees as shares of the vault to the fee recipient
     * @dev It will also update the all other fee related variables
     */
    function takeFees() external whenNotPaused {
        _takeFees();
    }

    /// @dev Internal function to take the fees
    function _takeFees() internal {
        Fees memory fees_ = fees;
        uint256 perfFee = _accruedPerformanceFee(fees_);
        uint256 mgmtFee = _accruedManagementFee(fees_);
        uint256 shareValue = convertToAssets(10 ** decimals);

        // Mint fees to the fee recipient
        if (perfFee + mgmtFee > 0)
            _mint(fees_.feeRecipient, convertToShares(perfFee + mgmtFee));

        // Update the high water mark (used by performance fee)
        if (shareValue > fees_.highWaterMark) fees.highWaterMark = shareValue;

        // Update the fees updated at timestamp (used by management fee)
        if (mgmtFee > 0) fees.feesUpdatedAt = uint64(block.timestamp);
    }

    /*//////////////////////////////////////////////////////////////
                          LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    Limits public limits;

    event LimitsUpdated(Limits prev, Limits next);

    /**
     * @notice Sets the deposit limit and minAmounts of the vault to limit user exposure to strategy risks
     * @param limits_ The limits to set
     */
    function setLimits(Limits memory limits_) external onlyOwner {
        _setLimits(limits_);
    }

    /// @dev Internal function to set the limits
    function _setLimits(Limits memory limits_) internal {
        // cache
        uint256 totalSupply_ = totalSupply;
        if (totalSupply_ > 0 && limits_.depositLimit < totalAssets())
            revert Misconfigured();
        if (limits_.minAmount > (10 ** decimals)) revert Misconfigured();

        emit LimitsUpdated(limits, limits_);

        limits = limits_;
    }
}
SafeTransferLib.sol 128 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}
FixedPointMathLib.sol 255 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}
IPriceOracle.sol 35 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

/// @title IPriceOracle
/// @custom:security-contact [email protected]
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Common PriceOracle interface.
interface IPriceOracle {
    /// @notice Get the name of the oracle.
    /// @return The name of the oracle.
    function name() external view returns (string memory);

    /// @notice One-sided price: How much quote token you would get for inAmount of base token, assuming no price spread.
    /// @param inAmount The amount of `base` to convert.
    /// @param base The token that is being priced.
    /// @param quote The token that is the unit of account.
    /// @return outAmount The amount of `quote` that is equivalent to `inAmount` of `base`.
    function getQuote(
        uint256 inAmount,
        address base,
        address quote
    ) external view returns (uint256 outAmount);

    /// @notice Two-sided price: How much quote token you would get/spend for selling/buying inAmount of base token.
    /// @param inAmount The amount of `base` to convert.
    /// @param base The token that is being priced.
    /// @param quote The token that is the unit of account.
    /// @return bidOutAmount The amount of `quote` you would get for selling `inAmount` of `base`.
    /// @return askOutAmount The amount of `quote` you would spend for buying `inAmount` of `base`.
    function getQuotes(
        uint256 inAmount,
        address base,
        address quote
    ) external view returns (uint256 bidOutAmount, uint256 askOutAmount);
}
BaseControlledAsyncRedeem.sol 576 lines
// SPDX-License-Identifier: GPL-3.0
// Docgen-SOLC: 0.8.25

pragma solidity ^0.8.25;

import {BaseERC7540} from "./BaseERC7540.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {IERC7540Redeem} from "ERC-7540/interfaces/IERC7540.sol";

/// @notice Stores the requestBalance of a controller
struct RequestBalance {
    /// @notice The amount of shares that have been requested to be redeemed
    uint256 pendingShares;
    /// @notice The timestamp of the last redeem request (will be used to ensure timely fulfillment of redeem requests)
    uint256 requestTime;
    /// @notice The amount of shares that have been freed up by a fulfilled redeem request
    uint256 claimableShares;
    /// @notice The amount of assets that have been freed up by a fulfilled redeem request
    uint256 claimableAssets;
}

/**
 * @title   BaseControlledAsyncRedeem
 * @author  RedVeil
 * @notice  Abstract contract containing reusable logic for controlled async redeem flows
 * @dev     Based on https://github.com/ERC4626-Alliance/ERC-7540-Reference/blob/main/src/BaseControlledAsyncRedeem.sol
 */
abstract contract BaseControlledAsyncRedeem is BaseERC7540, IERC7540Redeem {
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                        ERC4626 OVERRIDDEN LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Deposit assets into the vault
     * @param assets The amount of assets to deposit
     * @param receiver The address to receive the shares
     * @return shares The amount of shares received
     * @dev This function is synchronous and will revert if the vault is paused
     * @dev It will first use claimable balances of previous redeem requests before transferring assets from the sender
     */
    function deposit(
        uint256 assets,
        address receiver
    ) public override whenNotPaused returns (uint256 shares) {
        // Additional logic for inheriting contracts
        beforeDeposit(assets, shares);

        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        SafeTransferLib.safeTransferFrom(
            asset,
            msg.sender,
            address(this),
            assets
        );

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        // Additional logic for inheriting contracts
        afterDeposit(assets, shares);
    }

    /**
     * @notice Mints shares from the vault
     * @param shares The amount of shares to mint
     * @param receiver The address to receive the shares
     * @return assets The amount of assets deposited
     * @dev This function is synchronous and will revert if the vault is paused
     * @dev It will first use claimable balances of previous redeem requests before minting shares
     */
    function mint(
        uint256 shares,
        address receiver
    ) public override whenNotPaused returns (uint256 assets) {
        // Additional logic for inheriting contracts
        beforeDeposit(assets, shares);

        require(shares != 0, "ZERO_SHARES");

        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        SafeTransferLib.safeTransferFrom(
            asset,
            msg.sender,
            address(this),
            assets
        );

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        // Additional logic for inheriting contracts
        afterDeposit(assets, shares);
    }

    /// @dev Additional logic for inheriting contracts before depositing
    function beforeDeposit(uint256 assets, uint256 shares) internal virtual {}

    /**
     * @notice Withdraws assets from the vault which have beenpreviously freed up by a fulfilled redeem request
     * @param assets The amount of assets to withdraw
     * @param receiver The address to receive the assets
     * @param controller The controller to withdraw from
     * @return shares The amount of shares burned
     * @dev This function is asynchronous and will not revert if the vault is paused
     * @dev msg.sender must be the controller or an operator for the controller
     * @dev Requires sufficient claimableAssets in the controller's requestBalance
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address controller
    ) public virtual override returns (uint256 shares) {
        require(
            controller == msg.sender || isOperator[controller][msg.sender],
            "ERC7540Vault/invalid-caller"
        );
        require(assets != 0, "ZERO_ASSETS");

        RequestBalance storage currentBalance = requestBalances[controller];
        shares = assets.mulDivUp(
            currentBalance.claimableShares,
            currentBalance.claimableAssets
        );

        // Modify the currentBalance state accordingly
        _withdrawClaimableBalance(assets, currentBalance);

        // Additional logic for inheriting contracts
        beforeWithdraw(assets, shares);

        // Transfer assets to the receiver
        SafeTransferLib.safeTransfer(asset, receiver, assets);

        emit Withdraw(msg.sender, receiver, controller, assets, shares);

        // Additional logic for inheriting contracts
        afterWithdraw(assets, shares);
    }

    /**
     * @notice Modifies the currentBalance state to reflect a withdrawal of claimableAssets
     * @param assets The amount of assets to withdraw
     * @param currentBalance The requestBalance of the controller
     * @dev Claiming partially introduces precision loss. The user therefore receives a rounded down amount,
     * while the claimable balance is reduced by a rounded up amount.
     */
    function _withdrawClaimableBalance(
        uint256 assets,
        RequestBalance storage currentBalance
    ) internal {
        uint256 sharesUp = assets.mulDivUp(
            currentBalance.claimableShares,
            currentBalance.claimableAssets
        );

        currentBalance.claimableAssets -= assets;
        currentBalance.claimableShares = currentBalance.claimableShares >
            sharesUp
            ? currentBalance.claimableShares - sharesUp
            : 0;
    }

    /**
     * @notice Redeems shares from the vault which have beenpreviously freed up by a fulfilled redeem request
     * @param shares The amount of shares to redeem
     * @param receiver The address to receive the assets
     * @param controller The controller to redeem from
     * @return assets The amount of assets received
     * @dev This function is asynchronous and will not revert if the vault is paused
     * @dev msg.sender must be the controller or an operator for the controller
     * @dev Requires sufficient claimableShares in the controller's requestBalance
     */
    function redeem(
        uint256 shares,
        address receiver,
        address controller
    ) public virtual override returns (uint256 assets) {
        require(
            controller == msg.sender || isOperator[controller][msg.sender],
            "ERC7540Vault/invalid-caller"
        );
        require(shares != 0, "ZERO_SHARES");

        RequestBalance storage currentBalance = requestBalances[controller];
        assets = shares.mulDivDown(
            currentBalance.claimableAssets,
            currentBalance.claimableShares
        );

        // Modify the currentBalance state accordingly
        _redeemClaimableBalance(shares, currentBalance);

        // Additional logic for inheriting contracts
        beforeWithdraw(assets, shares);

        // Transfer assets to the receiver
        SafeTransferLib.safeTransfer(asset, receiver, assets);

        emit Withdraw(msg.sender, receiver, controller, assets, shares);

        // Additional logic for inheriting contracts
        afterWithdraw(assets, shares);
    }

    /**
     * @notice Modifies the currentBalance state to reflect a withdrawal of claimableAssets
     * @param shares The amount of shares to redeem
     * @param currentBalance The requestBalance of the controller
     * @dev Claiming partially introduces precision loss. The user therefore receives a rounded down amount,
     * while the claimable balance is reduced by a rounded up amount.
     */
    function _redeemClaimableBalance(
        uint256 shares,
        RequestBalance storage currentBalance
    ) internal {
        uint256 assetsUp = shares.mulDivUp(
            currentBalance.claimableAssets,
            currentBalance.claimableShares
        );

        currentBalance.claimableAssets = currentBalance.claimableAssets >
            assetsUp
            ? currentBalance.claimableAssets - assetsUp
            : 0;
        currentBalance.claimableShares -= shares;
    }

    /// @dev Additional logic for inheriting contracts after withdrawing
    function afterWithdraw(uint256 assets, uint256 shares) internal virtual {}

    /*//////////////////////////////////////////////////////////////
                        ACCOUNTNG LOGIC
    //////////////////////////////////////////////////////////////*/
    /// @dev controller => requestBalance
    mapping(address => RequestBalance) public requestBalances;

    /**
     * @notice Returns the requestBalance of a controller
     * @param controller The controller to get the requestBalance of
     * @return requestBalance The requestBalance of the controller
     */
    function getRequestBalance(
        address controller
    ) public view returns (RequestBalance memory) {
        return requestBalances[controller];
    }

    /**
     * @notice Returns the requested shares for redeem that have not yet been fulfilled of a controller
     * @param controller The controller to get the pendingShares of
     * @return pendingShares The pendingShares of the controller
     */
    function pendingRedeemRequest(
        uint256,
        address controller
    ) public view returns (uint256) {
        return requestBalances[controller].pendingShares;
    }

    /**
     * @notice Returns the shares that have been freed up by a fulfilled redeem request of a controller
     * @param controller The controller to get the claimableShares of
     * @return claimableShares The claimableShares of the controller
     */
    function claimableRedeemRequest(
        uint256,
        address controller
    ) public view returns (uint256) {
        return requestBalances[controller].claimableShares;
    }

    /**
     * @notice Simulates a deposit into the vault and returns the amount of shares that would be received by the user
     * @param assets The amount of assets to deposit
     * @return shares The amount of shares that would be received by the user
     * @dev This function will return 0 if the vault is paused
     */
    function previewDeposit(
        uint256 assets
    ) public view virtual override returns (uint256) {
        return paused ? 0 : super.previewDeposit(assets);
    }

    /**
     * @notice Simulates a mint into the vault and returns the amount of assets required to mint the given amount of shares
     * @param shares The amount of shares to mint
     * @return assets The amount of assets required to mint the given amount of shares
     * @dev This function will return 0 if the vault is paused
     */
    function previewMint(
        uint256 shares
    ) public view virtual override returns (uint256) {
        return paused ? 0 : super.previewMint(shares);
    }

    /// @dev Previewing withdraw is not supported for async flows (we would require the controller to be known which we do not have in ERC4626)
    function previewWithdraw(
        uint256
    ) public pure virtual override returns (uint256) {
        revert("ERC7540Vault/async-flow");
    }

    /// @dev Previewing redeem is not supported for async flows (we would require the controller to be known which we do not have in ERC4626)
    function previewRedeem(
        uint256
    ) public pure virtual override returns (uint256 assets) {
        revert("ERC7540Vault/async-flow");
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the maximum amount of assets that can be deposited into the vault
     * @return assets The maxDeposit of the controller
     * @dev Will return 0 if the vault is paused
     */
    function maxDeposit(
        address
    ) public view virtual override returns (uint256) {
        return paused ? 0 : type(uint256).max;
    }

    /**
     * @notice Returns the maximum amount of shares that can be minted into the vault
     * @return shares The maxMint of the controller
     * @dev Will return 0 if the vault is paused
     */
    function maxMint(address) public view virtual override returns (uint256) {
        return paused ? 0 : type(uint256).max;
    }

    /**
     * @notice Returns the maximum amount of assets that can be withdrawn from the vault
     * @param controller The controller to get the maxWithdraw of
     * @return assets The maxWithdraw of the controller
     * @dev This is simply the claimableAssets of the controller (i.e. the assets that have been freed up by a fulfilled redeem request)
     */
    function maxWithdraw(
        address controller
    ) public view virtual override returns (uint256) {
        return requestBalances[controller].claimableAssets;
    }

    /**
     * @notice Returns the maximum amount of shares that can be redeemed from the vault
     * @param controller The controller to get the maxRedeem of
     * @return shares The maxRedeem of the controller
     * @dev This is simply the claimableShares of the controller (i.e. the shares that have been freed up by a fulfilled redeem request)
     */
    function maxRedeem(
        address controller
    ) public view virtual override returns (uint256) {
        return requestBalances[controller].claimableShares;
    }

    /*//////////////////////////////////////////////////////////////
                        REQUEST REDEEM LOGIC
    //////////////////////////////////////////////////////////////*/

    event RedeemRequested(
        address indexed controller,
        address indexed owner,
        uint256 requestId,
        address sender,
        uint256 shares
    );

    /**
     * @notice Requests a redeem of shares from the vault
     * @param shares The amount of shares to redeem
     * @param controller The user that will be receiving pending shares
     * @param owner The owner of the shares to redeem
     * @return requestId The requestId of the redeem request
     * @dev This redeem request is added to any pending redeem request of the controller
     */
    function requestRedeem(
        uint256 shares,
        address controller,
        address owner
    ) external virtual returns (uint256 requestId) {
        return _requestRedeem(shares, controller, owner);
    }

    /// @dev Internal function to request a redeem
    function _requestRedeem(
        uint256 shares,
        address controller,
        address owner
    ) internal returns (uint256 requestId) {
        require(
            owner == msg.sender || isOperator[owner][msg.sender],
            "ERC7540Vault/invalid-owner"
        );
        require(
            ERC20(address(this)).balanceOf(owner) >= shares,
            "ERC7540Vault/insufficient-balance"
        );
        require(shares != 0, "ZERO_SHARES");

        // Transfer shares from owner to vault (these will be burned on withdrawal)
        SafeTransferLib.safeTransferFrom(this, owner, address(this), shares);

        // Update the controller's requestBalance
        RequestBalance storage currentBalance = requestBalances[controller];
        currentBalance.pendingShares += shares;
        currentBalance.requestTime = block.timestamp;

        emit RedeemRequested(controller, owner, REQUEST_ID, msg.sender, shares);
        return REQUEST_ID;
    }

    /*//////////////////////////////////////////////////////////////
                        CANCEL REDEEM REQUEST LOGIC
    //////////////////////////////////////////////////////////////*/

    event RedeemRequestCanceled(
        address indexed controller,
        address indexed receiver,
        uint256 shares
    );

    /**
     * @notice Cancels a redeem request of the controller
     * @param controller The controller to cancel the redeem request of
     * @dev This will transfer the pending shares back to the msg.sender
     */
    function cancelRedeemRequest(address controller) external virtual {
        return _cancelRedeemRequest(controller, msg.sender);
    }

    /**
     * @notice Cancels a redeem request of the controller
     * @param controller The controller to cancel the redeem request of
     * @param receiver The receiver of the pending shares
     * @dev This will transfer the pending shares back to the receiver
     */
    function cancelRedeemRequest(
        address controller,
        address receiver
    ) public virtual {
        return _cancelRedeemRequest(controller, receiver);
    }

    /// @dev Internal function to cancel a redeem request
    function _cancelRedeemRequest(
        address controller,
        address receiver
    ) internal virtual {
        require(
            controller == msg.sender || isOperator[controller][msg.sender],
            "ERC7540Vault/invalid-caller"
        );

        // Get the pending shares
        RequestBalance storage currentBalance = requestBalances[controller];
        uint256 shares = currentBalance.pendingShares;

        require(shares > 0, "ERC7540Vault/no-pending-request");

        // Transfer the pending shares back to the receiver
        SafeTransferLib.safeTransfer(ERC20(address(this)), receiver, shares);

        // Update the controller's requestBalance
        currentBalance.pendingShares = 0;
        currentBalance.requestTime = 0;

        emit RedeemRequestCanceled(controller, receiver, shares);
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT FULFILLMENT LOGIC
    //////////////////////////////////////////////////////////////*/

    event RedeemRequestFulfilled(
        address indexed controller,
        address indexed fulfiller,
        uint256 shares,
        uint256 assets
    );

    /**
     * @notice Fulfills a redeem request of the controller to allow the controller to withdraw their assets
     * @param shares The amount of shares to redeem
     * @param controller The controller to redeem for
     * @return assets The amount of assets claimable by the controller
     */
    function fulfillRedeem(
        uint256 shares,
        address controller
    ) external virtual returns (uint256) {
        uint256 assets = convertToAssets(shares);

        // Burn controller's shares
        _burn(address(this), shares);

        return _fulfillRedeem(assets, shares, controller);
    }

    /// @dev Internal function to fulfill a redeem request
    function _fulfillRedeem(
        uint256 assets,
        uint256 shares,
        address controller
    ) internal virtual returns (uint256) {
        if (assets == 0 || shares == 0) revert("ZERO_SHARES");

        RequestBalance storage currentBalance = requestBalances[controller];

        // Check that there are pending shares to fulfill
        require(
            currentBalance.pendingShares != 0 &&
                shares <= currentBalance.pendingShares,
            "ZERO_SHARES"
        );

        // Additional logic for inheriting contracts
        beforeFulfillRedeem(assets, shares);

        // Update the controller's requestBalance
        currentBalance.claimableShares += shares;
        currentBalance.claimableAssets += assets;
        currentBalance.pendingShares -= shares;

        // Reset the requestTime if there are no more pending shares
        if (currentBalance.pendingShares == 0) currentBalance.requestTime = 0;

        emit RedeemRequestFulfilled(controller, msg.sender, shares, assets);

        // Additional logic for inheriting contracts
        afterFulfillRedeem(assets, shares);

        return assets;
    }

    /// @dev Additional logic for inheriting contracts before fulfilling a redeem request
    function beforeFulfillRedeem(
        uint256 assets,
        uint256 shares
    ) internal virtual {}

    /// @dev Additional logic for inheriting contracts after fulfilling a redeem request
    function afterFulfillRedeem(
        uint256 assets,
        uint256 shares
    ) internal virtual {}

    /*//////////////////////////////////////////////////////////////
                        ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Check if the contract supports an interface
     * @param interfaceId The interface ID to check
     * @return exists True if the contract supports the interface, false otherwise
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public pure virtual override returns (bool) {
        return
            interfaceId == type(IERC7540Redeem).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}
BaseERC7540.sol 233 lines
// SPDX-License-Identifier: GPL-3.0
// Docgen-SOLC: 0.8.25

pragma solidity ^0.8.25;

import {ERC4626} from "solmate/tokens/ERC4626.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol";
import {Owned} from "src/utils/Owned.sol";
import {Pausable} from "src/utils/Pausable.sol";
import {IERC7540Operator} from "ERC-7540/interfaces/IERC7540.sol";
import {IERC7575} from "ERC-7540/interfaces/IERC7575.sol";
import {IERC165} from "ERC-7540/interfaces/IERC7575.sol";

/**
 * @title   BaseERC7540
 * @author  RedVeil
 * @notice  Abstract contract containing reusable logic for ERC-7540 - https://eips.ethereum.org/EIPS/eip-7540
 * @notice  Based on https://github.com/ERC4626-Alliance/ERC-7540-Reference/blob/main/src/BaseERC7540.sol
 */
abstract contract BaseERC7540 is
    ERC4626,
    Owned,
    ReentrancyGuard,
    Pausable,
    IERC7540Operator
{
    /// @dev Assume requests are non-fungible and all have ID = 0
    uint256 internal constant REQUEST_ID = 0;

    /// @dev Required for IERC7575
    address public share = address(this);

    /**
     * @notice Constructor for BaseERC7540
     * @param _owner The permissioned owner of the vault (controls all management functions)
     * @param _asset The address of the underlying asset
     * @param _name The name of the vault
     * @param _symbol The symbol of the vault
     */
    constructor(
        address _owner,
        address _asset,
        string memory _name,
        string memory _symbol
    ) Owned(_owner) ERC4626(ERC20(_asset), _name, _symbol) {}

    /*//////////////////////////////////////////////////////////////
                            ROLE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @dev role => account => approved
    mapping(bytes32 => mapping(address => bool)) public hasRole;

    event RoleUpdated(bytes32 role, address account, bool approved);

    /**
     * @notice Update the role for an account
     * @param role The role to update
     * @param account The account to update
     * @param approved The approval status to set
     */
    function updateRole(
        bytes32 role,
        address account,
        bool approved
    ) public onlyOwner {
        hasRole[role][account] = approved;

        emit RoleUpdated(role, account, approved);
    }

    /**
     * @notice Modifier to check if the caller has the specified role or is the owner
     * @param role The role to check
     */
    modifier onlyRoleOrOwner(bytes32 role) {
        require(
            hasRole[role][msg.sender] || msg.sender == owner,
            "BaseERC7540/not-authorized"
        );
        _;
    }

    /*//////////////////////////////////////////////////////////////
                            PAUSING LOGIC
    //////////////////////////////////////////////////////////////*/

    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    /// @notice Pause Deposits. Caller must be owner or have the PAUSER_ROLE
    function pause() external override onlyRoleOrOwner(PAUSER_ROLE) {
        _pause();
    }

    /// @notice Unpause Deposits. Caller must be owner
    function unpause() external override onlyOwner {
        _unpause();
    }

    /*//////////////////////////////////////////////////////////////
                        ERC7540 LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @dev controller => operator => approved
    mapping(address => mapping(address => bool)) public isOperator;

    /**
     * @notice Set the approval status for an operator
     * @param operator The operator to set
     * @param approved The approval status to set
     * @dev Operators are approved to requestRedeem,withdraw and redeem for the msg.sender using the balance of msg.sender
     */
    function setOperator(
        address operator,
        bool approved
    ) public virtual returns (bool success) {
        require(
            msg.sender != operator,
            "ERC7540Vault/cannot-set-self-as-operator"
        );
        isOperator[msg.sender][operator] = approved;
        emit OperatorSet(msg.sender, operator, approved);
        success = true;
    }

    /*//////////////////////////////////////////////////////////////
                        EIP-7441 LOGIC
    //////////////////////////////////////////////////////////////*/

    mapping(address controller => mapping(bytes32 nonce => bool used))
        public authorizations;

    /**
     * @notice Authorize an operator for a controller
     * @param controller The controller to authorize the operator for
     * @param operator The operator to authorize
     * @param approved The approval status to set
     * @param nonce The nonce to use for the authorization
     * @param deadline The deadline for the authorization
     * @param signature The signature to verify the authorization
     * @dev Operators are approved to requestRedeem,withdraw and redeem for the msg.sender using the balance of msg.sender
     */
    function authorizeOperator(
        address controller,
        address operator,
        bool approved,
        bytes32 nonce,
        uint256 deadline,
        bytes memory signature
    ) public virtual returns (bool success) {
        require(
            controller != operator,
            "ERC7540Vault/cannot-set-self-as-operator"
        );
        require(block.timestamp <= deadline, "ERC7540Vault/expired");
        require(
            !authorizations[controller][nonce],
            "ERC7540Vault/authorization-used"
        );

        authorizations[controller][nonce] = true;

        bytes32 r;
        bytes32 s;
        uint8 v;
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        require(
            uint256(s) <=
                0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
            "ERC7540Vault/invalid-signature-s"
        );

        address recoveredAddress = ecrecover(
            keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR(),
                    keccak256(
                        abi.encode(
                            keccak256(
                                "AuthorizeOperator(address controller,address operator,bool approved,bytes32 nonce,uint256 deadline)"
                            ),
                            controller,
                            operator,
                            approved,
                            nonce,
                            deadline
                        )
                    )
                )
            ),
            v,
            r,
            s
        );

        require(
            recoveredAddress != address(0) && recoveredAddress == controller,
            "INVALID_SIGNER"
        );

        isOperator[controller][operator] = approved;

        emit OperatorSet(controller, operator, approved);

        success = true;
    }

    /*//////////////////////////////////////////////////////////////
                        ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Check if the contract supports an interface
     * @param interfaceId The interface ID to check
     * @return True if the contract supports the interface, false otherwise
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public pure virtual returns (bool) {
        return
            interfaceId == type(IERC7575).interfaceId ||
            interfaceId == type(IERC7540Operator).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
}
ERC20.sol 206 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}
IERC7540.sol 138 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

interface IERC7540Operator {
    /**
     * @dev The event emitted when an operator is set.
     *
     * @param controller The address of the controller.
     * @param operator The address of the operator.
     * @param approved The approval status.
     */
    event OperatorSet(address indexed controller, address indexed operator, bool approved);

    /**
     * @dev Sets or removes an operator for the caller.
     *
     * @param operator The address of the operator.
     * @param approved The approval status.
     * @return Whether the call was executed successfully or not
     */
    function setOperator(address operator, bool approved) external returns (bool);

    /**
     * @dev Returns `true` if the `operator` is approved as an operator for an `controller`.
     *
     * @param controller The address of the controller.
     * @param operator The address of the operator.
     * @return status The approval status
     */
    function isOperator(address controller, address operator) external view returns (bool status);
}

interface IERC7540Deposit {
    event DepositRequest(
        address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    );
    /**
     * @dev Transfers assets from sender into the Vault and submits a Request for asynchronous deposit.
     *
     * - MUST support ERC-20 approve / transferFrom on asset as a deposit Request flow.
     * - MUST revert if all of assets cannot be requested for deposit.
     * - owner MUST be msg.sender unless some unspecified explicit approval is given by the caller,
     *    approval of ERC-20 tokens from owner to sender is NOT enough.
     *
     * @param assets the amount of deposit assets to transfer from owner
     * @param controller the controller of the request who will be able to operate the request
     * @param owner the source of the deposit assets
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token.
     */

    function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId);

    /**
     * @dev Returns the amount of requested assets in Pending state.
     *
     * - MUST NOT include any assets in Claimable state for deposit or mint.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function pendingDepositRequest(uint256 requestId, address controller)
        external
        view
        returns (uint256 pendingAssets);

    /**
     * @dev Returns the amount of requested assets in Claimable state for the controller to deposit or mint.
     *
     * - MUST NOT include any assets in Pending state.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function claimableDepositRequest(uint256 requestId, address controller)
        external
        view
        returns (uint256 claimableAssets);

    /**
     * @dev Mints shares Vault shares to receiver by claiming the Request of the controller.
     *
     * - MUST emit the Deposit event.
     * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator.
     */
    function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares);

    /**
     * @dev Mints exactly shares Vault shares to receiver by claiming the Request of the controller.
     *
     * - MUST emit the Deposit event.
     * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator.
     */
    function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets);
}

interface IERC7540Redeem {
    event RedeemRequest(
        address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    );

    /**
     * @dev Assumes control of shares from sender into the Vault and submits a Request for asynchronous redeem.
     *
     * - MUST support a redeem Request flow where the control of shares is taken from sender directly
     *   where msg.sender has ERC-20 approval over the shares of owner.
     * - MUST revert if all of shares cannot be requested for redeem.
     *
     * @param shares the amount of shares to be redeemed to transfer from owner
     * @param controller the controller of the request who will be able to operate the request
     * @param owner the source of the shares to be redeemed
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault's share token.
     */
    function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId);

    /**
     * @dev Returns the amount of requested shares in Pending state.
     *
     * - MUST NOT include any shares in Claimable state for redeem or withdraw.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function pendingRedeemRequest(uint256 requestId, address controller)
        external
        view
        returns (uint256 pendingShares);

    /**
     * @dev Returns the amount of requested shares in Claimable state for the controller to redeem or withdraw.
     *
     * - MUST NOT include any shares in Pending state for redeem or withdraw.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function claimableRedeemRequest(uint256 requestId, address controller)
        external
        view
        returns (uint256 claimableShares);
}
ERC4626.sol 183 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

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

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}
ReentrancyGuard.sol 19 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() virtual {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}
Owned.sol 50 lines
// SPDX-License-Identifier: GPL-3.0
// Docgen-SOLC: 0.8.25

pragma solidity ^0.8.25;

// https://docs.synthetix.io/contracts/source/contracts/owned
contract Owned {
    address public owner;
    address public nominatedOwner;

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);

    constructor(address _owner) {
        require(_owner != address(0), "Owned/owner-zero");
        owner = _owner;

        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external virtual onlyOwner {
        nominatedOwner = _owner;

        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external virtual {
        require(
            msg.sender == nominatedOwner,
            "Owned/not-nominated"
        );

        emit OwnerChanged(owner, nominatedOwner);

        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner() {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(
            msg.sender == owner,
            "Owned/not-owner"
        );
    }
}
Pausable.sol 54 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized Pausable for smart contracts.
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Pausable.sol)
abstract contract Pausable {
    bool public paused;

    event Paused(address account);
    event Unpaused(address account);

    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    modifier whenPaused() {
        _requirePaused();
        _;
    }

    function _requireNotPaused() internal view virtual {
        if (paused) {
            revert("Pausable/paused");
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused) {
            revert("Pausable/not-paused");
        }
    }

    function _pause() internal virtual whenNotPaused {
        paused = true;
        emit Paused(msg.sender);
    }

    function _unpause() internal virtual whenPaused {
        paused = false;
        emit Unpaused(msg.sender);
    }

    function pause() external virtual whenNotPaused {
        _pause();
    }

    function unpause() external virtual whenPaused {
        _unpause();
    }
}
IERC7575.sol 243 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

/**
 * @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.
 */
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);
}

interface IERC7575 is IERC165 {
    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 address of the share token
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function share() external view returns (address shareTokenAddress);

    /**
     * @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 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 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);
}

Read Contract

DOMAIN_SEPARATOR 0x3644e515 → bytes32
PAUSER_ROLE 0xe63ab1e9 → bytes32
accruedFees 0x682c2058 → uint256
allowance 0xdd62ed3e → uint256
asset 0x38d52e0f → address
authorizations 0xcdf5bba3 → bool
balanceOf 0x70a08231 → uint256
bounds 0x5843a5ad → uint256, uint256
claimableRedeemRequest 0xeaed1d07 → uint256
convertToAssets 0x07a2d13a → uint256
convertToLowBoundAssets 0x26537b57 → uint256
convertToShares 0xc6e6f592 → uint256
decimals 0x313ce567 → uint8
fees 0x9af1d35a → uint64, uint64, uint64, uint64, uint256, address
getBounds 0x3cc87a20 → tuple
getFees 0xdb8d55f1 → tuple
getRequestBalance 0x7d7b81b7 → tuple
hasRole 0x91d14854 → bool
isOperator 0xb6363cf2 → bool
limits 0x860aefcf → uint256, uint256
maxDeposit 0x402d267d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxWithdraw 0xce96cb77 → uint256
name 0x06fdde03 → string
nominatedOwner 0x53a47bb7 → address
nonces 0x7ecebe00 → uint256
oracle 0x7dc0d1d0 → address
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
pendingRedeemRequest 0xf5a23d8d → uint256
previewDeposit 0xef8b30f7 → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewWithdraw 0x0a28a477 → uint256
proposedOracle 0x78d87328 → address, uint256
proposedSafe 0x9a6d1866 → address, uint256
requestBalances 0x138e027c → uint256, uint256, uint256, uint256
safe 0x186f0354 → address
share 0xa8d5fd65 → address
supportsInterface 0x01ffc9a7 → bool
symbol 0x95d89b41 → string
totalAssets 0x01e1d114 → uint256
totalSupply 0x18160ddd → uint256

Write Contract 33 functions

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

acceptOracle 0x7f2d9575
No parameters
acceptOwnership 0x79ba5097
No parameters
acceptSafe 0x7ed51b77
No parameters
approve 0x095ea7b3
address spender
uint256 amount
returns: bool
authorizeOperator 0x711b58ff
address controller
address operator
bool approved
bytes32 nonce
uint256 deadline
bytes signature
returns: bool
cancelRedeemRequest 0x2d0777d8
address controller
address receiver
cancelRedeemRequest 0x99998406
address controller
deposit 0x6e553f65
uint256 assets
address receiver
returns: uint256
deposit 0xb6b55f25
uint256 assets
returns: uint256
fulfillMultipleRedeems 0xce955960
uint256[] shares
address[] controllers
returns: uint256
fulfillRedeem 0x126bd46c
uint256 shares
address controller
returns: uint256
mint 0x94bf804d
uint256 shares
address receiver
returns: uint256
mint 0xa0712d68
uint256 shares
returns: uint256
nominateNewOwner 0x1627540c
address _owner
pause 0x8456cb59
No parameters
permit 0xd505accf
address owner
address spender
uint256 value
uint256 deadline
uint8 v
bytes32 r
bytes32 s
proposeOracle 0xadc565e1
address newOracle
proposeSafe 0x2aa38127
address newSafe
redeem 0xba087652
uint256 shares
address receiver
address controller
returns: uint256
redeem 0xdb006a75
uint256 shares
returns: uint256
requestRedeem 0x7d41c86e
uint256 shares
address controller
address owner
returns: uint256
requestRedeem 0xaa2f892d
uint256 shares
returns: uint256
setBounds 0x060589a7
tuple bounds_
setFees 0xa68cff8d
tuple fees_
setLimits 0xdd0790d4
tuple limits_
setOperator 0x558a7297
address operator
bool approved
returns: bool
takeFees 0x5dd912f5
No parameters
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
unpause 0x3f4ba83a
No parameters
updateRole 0x346080af
bytes32 role
address account
bool approved
withdraw 0x2e1a7d4d
uint256 assets
returns: uint256
withdraw 0xb460af94
uint256 assets
address receiver
address controller
returns: uint256

Recent Transactions

No transactions found for this address