Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x2fEbfdE8B5e2d779420643E055CaC9df5DBC4799
Balance 0 ETH
Nonce 1
Code Size 13254 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

13254 bytes
0x608060405234801561000f575f80fd5b506004361061039d575f3560e01c806384ce2bf7116101ea578063ba08765211610114578063d905777e116100a9578063ebf0c71711610079578063ebf0c717146108e9578063ef8b30f71461041a578063f15b65b114610910578063f5a23d8d14610923575f80fd5b8063d905777e14610889578063da39b3e71461089c578063e2fdcc17146108af578063eaed1d07146108d6575f80fd5b8063cd0d0096116100e4578063cd0d00961461080f578063cdf5bba314610836578063ce96cb7714610863578063d4e8be8314610876575f80fd5b8063ba087652146107b7578063bf353dbb146107ca578063c63d75b6146107e9578063c6e6f592146107fc575f80fd5b8063a8d5fd651161018a578063b460af941161015a578063b460af941461071b578063b6363cf21461072e578063b7adb4c51461075b578063b9cf0634146107a4575f80fd5b8063a8d5fd65146106ce578063aabb5922146106f5578063b04a5e0514610708578063b3d7f6b91461041a575f80fd5b806399530b06116101c557806399530b061461068d578063995ea21a146106955780639c52a7f1146106a8578063a21ec79f146106bb575f80fd5b806384ce2bf71461065457806385b77f451461066757806394bf804d1461067a575f80fd5b80633e0dc34e116102cb5780635f3e849f1161026b578063711b58ff1161023b578063711b58ff1461060857806378d77ecb1461061b5780637c1b50c41461062e5780637d41c86e14610641575f80fd5b80635f3e849f146105bc57806365fae35e146105cf57806369d77a44146105e25780636e553f65146105f5575f80fd5b80634b5fd666116102a65780634b5fd6661461058e5780634cdad5061461041a57806352aaead8146105a1578063558a7297146105a9575f80fd5b80633e0dc34e14610528578063402d267d14610568578063481c6a751461057b575f80fd5b806321f075ee116103415780632cdfbae5116103115780632cdfbae5146104bb5780632e2d2984146104ce5780633644e515146104e157806338d52e0f146104e9575f80fd5b806321f075ee1461044f578063234f0e3b1461046257806326c6f96c146104955780632b9d9c1f146104a8575f80fd5b80630263704b1161037c5780630263704b146103f257806307a2d13a146104075780630a28a4771461041a5780630d62c33214610428575f80fd5b8062a06d19146103a157806301e1d114146103c757806301ffc9a7146103cf575b5f80fd5b6103b46103af366004612efb565b610936565b6040519081526020015b60405180910390f35b6103b4610a11565b6103e26103dd366004612f49565b610a9a565b60405190151581526020016103be565b610405610400366004612f64565b610b8d565b005b6103b4610415366004612f94565b610c32565b6103b461039d366004612f94565b6103b47fa3efcf8cb518126a85cdfd1c1102ee539e0700189f80926e1ac37144450473fa81565b61040561045d366004612fb8565b610ca4565b610405610470366004612f94565b335f90815260026020908152604080832093835292905220805460ff19166001179055565b6103b46104a3366004612fed565b610e4b565b6104056104b6366004612fed565b610ec5565b6104056104c9366004613017565b610f71565b6103b46104dc366004612efb565b611015565b6103b4611100565b6105107f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6040516001600160a01b0390911681526020016103be565b61054f7f00000000000000000000000000000000000000000000000000000000604eaa0f81565b60405167ffffffffffffffff90911681526020016103be565b6103b4610576366004613051565b6111f1565b600154610510906001600160a01b031681565b61040561059c36600461306a565b611229565b61054f6112b9565b6103e26105b7366004612fb8565b611323565b6104056105ca366004613017565b6113fd565b6104056105dd366004613051565b611461565b6103b46105f0366004612efb565b6114f9565b6103b4610603366004612fed565b6115c9565b6103e26106163660046130a6565b6115d5565b6103e2610629366004613051565b6118c9565b6103e261063c366004612fed565b611964565b6103b461064f366004612efb565b6119d7565b61040561066236600461306a565b611efb565b6103b4610675366004612efb565b611f8b565b6103b4610688366004612fed565b612273565b6103b461227f565b6103b46106a3366004612fed565b6122ae565b6104056106b6366004613051565b6122b8565b6104056106c9366004612f64565b61234f565b6105107f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d81565b6103b4610703366004612fed565b6123e6565b6103b4610716366004612fed565b61241e565b6103b4610729366004612efb565b612456565b6103e261073c36600461319d565b600360209081525f928352604080842090915290825290205460ff1681565b6107827fda64aae939e4d3a981004619f1709d8f0000000000000000000000000000000081565b6040516fffffffffffffffffffffffffffffffff1990911681526020016103be565b6104056107b2366004612fed565b61253c565b6103b46107c5366004612efb565b6125e8565b6103b46107d8366004613051565b5f6020819052908152604090205481565b6103b46107f7366004613051565b6126ce565b6103b461080a366004612f94565b612706565b6103b47f000000000000000000000000000000000000000000000000000000000000000181565b6103e261084436600461306a565b600260209081525f928352604080842090915290825290205460ff1681565b6103b4610871366004613051565b61273d565b610405610884366004612fed565b612775565b6103b4610897366004613051565b61289d565b6103b46108aa366004612efb565b6128d5565b6105107f0000000000000000000000000000000005f458fd6ba9eeb5f365d83b7da913dd81565b6103b46108e4366004612fed565b6129b1565b6105107f0000000000000000000000000c1fdfd6a1331a875ea013f3897fc8a76ada5dfc81565b6103e261091e366004612fed565b6129bb565b6103b4610931366004612fed565b6129f3565b5f61094082612a2b565b6001546040516311dcd79160e01b81523060048201526001600160a01b0385811660248301528481166044830152909116906311dcd791906064016020604051808303815f875af1158015610997573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109bb91906131c5565b60408051338152602081018390529192505f916001600160a01b0380861692908716917f9c133d4657dc9cd12f4c08cef86ef778dbbe03f3ad3b661ff14d36bc3febb1fb91015b60405180910390a49392505050565b5f610a957f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a71573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061041591906131c5565b905090565b5f6001600160e01b03198216630ce3bbe560e41b1480610aca57506001600160e01b03198216631883ba3960e21b145b80610ae557506001600160e01b0319821663e3bc4e6560e01b145b80610b0057506001600160e01b03198216638bf840e360e01b145b80610b1b57506001600160e01b0319821663e76cffc760e01b145b80610b3657506001600160e01b03198216632f0a18c560e01b145b80610b5157506001600160e01b031982166354f2843960e11b145b80610b6c57506001600160e01b031982166378d77ecb60e01b145b80610b8757506001600160e01b031982166301ffc9a760e01b145b92915050565b335f90815260208190526040902054600114610be65760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b60448201526064015b60405180910390fd5b60408051838152602081018390525f916001600160a01b038616917f4dd5187225a2ae5f5ea35ca7b1732180f848cc4b6f7dce34b4c5e9f384d77dec91015b60405180910390a3505050565b6001546040516350603df360e01b8152306004820152602481018390525f916001600160a01b0316906350603df3906044015b602060405180830381865afa158015610c80573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b8791906131c5565b6001600160a01b0382163303610d0d5760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201526737b832b930ba37b960c11b6064820152608401610bdd565b60405163854b89d560e01b81523360048201527f0000000000000000000000000c1fdfd6a1331a875ea013f3897fc8a76ada5dfc6001600160a01b03169063854b89d590602401602060405180830381865afa158015610d6f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9391906131dc565b610ddf5760405162461bcd60e51b815260206004820152601960248201527f455243373534305661756c742f6e6f742d656e646f72736564000000000000006044820152606401610bdd565b6001600160a01b0382165f8181526003602090815260408083203380855290835292819020805460ff191686151590811790915590519081529192917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa26791015b60405180910390a35050565b6001546040516306ff657760e31b81523060048201526001600160a01b0383811660248301525f9216906337fb2bb8906044015b602060405180830381865afa158015610e9a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ebe91906131c5565b9392505050565b610ece81612a2b565b600154604051638b44deb160e01b81523060048201526001600160a01b03838116602483015233604483015290911690638b44deb1906064015f604051808303815f87803b158015610f1e575f80fd5b505af1158015610f30573d5f803e3d5ffd5b50506040513381525f92506001600160a01b03841691507fa16c0f2cab616ed5d17cd544655b00d7062a1df7c457960cc8b9bf60770f923690602001610e3f565b335f90815260208190526040902054600114610fc55760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b60408051338152602081018390525f916001600160a01b0380861692908716917f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc574506910160405180910390a4505050565b5f61101f82612a2b565b60015460405163c6f1649f60e01b8152306004820152602481018690526001600160a01b03858116604483015284811660648301529091169063c6f1649f906084016020604051808303815f875af115801561107d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110a191906131c5565b9050816001600160a01b0316836001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d786846040516110f1929190918252602082015260400190565b60405180910390a39392505050565b5f7f000000000000000000000000000000000000000000000000000000000000000146146111cc5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fe416b338a274162320c79445ae6604141d1cb08275eb27011b69f002dc094d05828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b507fbbc34cd3fea0ef0a742c4c1353bbd1ddc36cb6d79dc38cdb0b3b94160a244ecb90565b600154604051630760168760e41b81523060048201526001600160a01b0383811660248301525f921690637601687090604401610c65565b335f9081526020819052604090205460011461127d5760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b5f826001600160a01b03167fe60d0cd4e01ab7d1e0cf1917bddb6779ca8db0fb23138e3b7a4bab6a727b966183604051610e3f91815260200190565b6001546040516308332b4560e11b81523060048201525f916001600160a01b031690631066568a90602401602060405180830381865afa1580156112ff573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a9591906131f7565b5f6001600160a01b038316330361138d5760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201526737b832b930ba37b960c11b6064820152608401610bdd565b335f8181526003602090815260408083206001600160a01b03881680855290835292819020805460ff191687151590811790915590519081529192917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a350600192915050565b335f908152602081905260409020546001146114515760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b61145c838383612ab3565b505050565b335f908152602081905260409020546001146114b55760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b6001600160a01b0381165f8181526020819052604080822060019055517fdd0e34038ac38b2a1ce960229778ac48a8719bc900b6c4f8d0475c6e8b385a609190a250565b5f61150382612a2b565b6001546040516339517e6560e11b81523060048201526001600160a01b0385811660248301528481166044830152909116906372a2fcca906064016020604051808303815f875af115801561155a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061157e91906131c5565b60408051338152602081018390529192505f916001600160a01b0380861692908716917f295a9a8b44c0ac92394b0da92ef469784ff064156f08dab8316760c064c6cd2f9101610a02565b5f610ebe838333611015565b5f856001600160a01b0316876001600160a01b0316036116485760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201526737b832b930ba37b960c11b6064820152608401610bdd565b824211156116985760405162461bcd60e51b815260206004820152601460248201527f455243373534305661756c742f657870697265640000000000000000000000006044820152606401610bdd565b6001600160a01b0387165f90815260026020908152604080832087845290915290205460ff161561170b5760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f617574686f72697a6174696f6e2d75736564006044820152606401610bdd565b6001600160a01b0387165f9081526002602090815260408083208784529091528120805460ff19166001179055611740611100565b604080517fa3efcf8cb518126a85cdfd1c1102ee539e0700189f80926e1ac37144450473fa60208201526001600160a01b03808c16928201929092529089166060820152871515608082015260a0810187905260c0810186905260e001604051602081830303815290604052805190602001206040516020016117da92919061190160f01b81526002810192909252602282015260420190565b6040516020818303038152906040528051906020012090506117fd888285612bd3565b6118545760405162461bcd60e51b815260206004820152602260248201527f455243373534305661756c742f696e76616c69642d617574686f72697a61746960448201526137b760f11b6064820152608401610bdd565b6001600160a01b038881165f818152600360209081526040808320948c1680845294825291829020805460ff19168b151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a3506001979650505050505050565b604051630fa1e71360e41b81525f600482018190526001600160a01b0383811660248401526044830182905290917f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d9091169063fa1e713090606401602060405180830381865afa158015611940573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b8791906131dc565b60015460405163010476c960e71b81523060048201526001600160a01b0383811660248301525f92169063823b6480906044015b602060405180830381865afa1580156119b3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ebe91906131dc565b6040516370a0823160e01b81526001600160a01b0382811660048301525f9185917f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d16906370a0823190602401602060405180830381865afa158015611a3f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a6391906131c5565b1015611abb5760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f696e73756666696369656e742d62616c616e636044820152606560f81b6064820152608401610bdd565b6001600160a01b0382165f90815260036020908152604080832033845290915281205460ff16611aeb5733611aed565b825b6001546040516321d26ff560e21b8152306004820152602481018890526001600160a01b03878116604483015286811660648301528084166084830152929350911690638749bfd49060a4016020604051808303815f875af1158015611b55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b7991906131dc565b611bd05760405162461bcd60e51b815260206004820152602260248201527f455243373534305661756c742f726571756573742d72656465656d2d6661696c604482015261195960f21b6064820152608401610bdd565b604051630fa1e71360e41b81526001600160a01b0384811660048301527f0000000000000000000000000000000005f458fd6ba9eeb5f365d83b7da913dd81166024830152604482018790527f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d169063fa1e713090606401602060405180830381865afa158015611c63573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c8791906131dc565b611cd35760405162461bcd60e51b815260206004820181905260248201527f455243373534305661756c742f7265737472696374696f6e732d6661696c65646044820152606401610bdd565b60405163887ca24960e01b81526001600160a01b03828116600483015284811660248301527f0000000000000000000000000000000005f458fd6ba9eeb5f365d83b7da913dd81166044830152606482018790527f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d169063887ca249906084016020604051808303815f875af1925050508015611d8d575060408051601f3d908101601f19168201909252611d8a918101906131dc565b60015b611ea4576040516323b872dd60e01b81526001600160a01b0384811660048301527f0000000000000000000000000000000005f458fd6ba9eeb5f365d83b7da913dd81166024830152604482018790527f000000000000000000000000fb9c180c2582ffc69b7f7c0ff7ee9b1d8ebc6f0d16906323b872dd906064016020604051808303815f875af1158015611e25573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e4991906131dc565b611e9f5760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f7472616e736665722d66726f6d2d6661696c656044820152601960fa1b6064820152608401610bdd565b611ea6565b505b60408051338152602081018790525f916001600160a01b0380871692908816917f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc574506910160405180910390a4505f949350505050565b335f90815260208190526040902054600114611f4f5760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b5f826001600160a01b03167f0ede66e1b0b7bef549c7d3a5c885444b371535620c089dc7b92ddf8550edaa1983604051610e3f91815260200190565b5f6001600160a01b038216331480611fc557506001600160a01b0382165f90815260036020908152604080832033845290915290205460ff165b6120115760405162461bcd60e51b815260206004820152601a60248201527f455243373534305661756c742f696e76616c69642d6f776e65720000000000006044820152606401610bdd565b6040516370a0823160e01b81526001600160a01b03838116600483015285917f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48909116906370a0823190602401602060405180830381865afa158015612079573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061209d91906131c5565b10156120f55760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f696e73756666696369656e742d62616c616e636044820152606560f81b6064820152608401610bdd565b600154604051620bcc0d60e41b8152306004820152602481018690526001600160a01b03858116604483015284811660648301523360848301529091169062bcc0d09060a4016020604051808303815f875af1158015612157573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061217b91906131dc565b6121d35760405162461bcd60e51b815260206004820152602360248201527f455243373534305661756c742f726571756573742d6465706f7369742d6661696044820152621b195960ea1b6064820152608401610bdd565b61221f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48837f0000000000000000000000000000000005f458fd6ba9eeb5f365d83b7da913dd87612db1565b60408051338152602081018690525f916001600160a01b0380861692908716917fbb58420bb8ce44e11b84e214cc0de10ce5e7c24d0355b2815c3d758b514cae72910160405180910390a4505f9392505050565b5f610ebe8383336128d5565b5f610a956104157f0000000000000000000000000000000000000000000000000000000000000006600a613315565b5f610ebe826111f1565b335f9081526020819052604090205460011461230c5760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b6001600160a01b0381165f81815260208190526040808220829055517f184450df2e323acec0ed3b5c7531b81f9b4cdef7914dfd4c0a4317416bb5251b9190a250565b335f908152602081905260409020546001146123a35760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b60408051838152602081018390525f916001600160a01b038616917fad23d6d908f14df9b90c8c580954f8a95c4a6e3261e49af08db21e6aaadcd1289101610c25565b600154604051631495d74f60e01b81523060048201526001600160a01b0383811660248301525f921690631495d74f90604401610e7f565b6001546040516308a44dc760e21b81523060048201526001600160a01b0383811660248301525f921690632291371c90604401610e7f565b5f61246082612a2b565b600154604051636fe6a09760e11b8152306004820152602481018690526001600160a01b03858116604483015284811660648301529091169063dfcd412e906084016020604051808303815f875af11580156124be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124e291906131c5565b9050816001600160a01b0316836001600160a01b0316336001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db8785604051610a02929190918252602082015260400190565b61254581612a2b565b60015460405163230096f760e21b81523060048201526001600160a01b03838116602483015233604483015290911690638c025bdc906064015f604051808303815f87803b158015612595575f80fd5b505af11580156125a7573d5f803e3d5ffd5b50506040513381525f92506001600160a01b03841691507f4ae0134aad5d0e98e82f45680facd628ee6b5aa85ad9a85248a7ef47319f8c5090602001610e3f565b5f6125f282612a2b565b60015460405163fea53be160e01b8152306004820152602481018690526001600160a01b03858116604483015284811660648301529091169063fea53be1906084016020604051808303815f875af1158015612650573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061267491906131c5565b9050816001600160a01b0316836001600160a01b0316336001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db8488604051610a02929190918252602082015260400190565b60015460405163f2e586db60e01b81523060048201526001600160a01b0383811660248301525f92169063f2e586db90604401610c65565b600154604051633e5541f160e01b8152306004820152602481018390525f916001600160a01b031690633e5541f190604401610c65565b6001546040516308c22e9b60e41b81523060048201526001600160a01b0383811660248301525f921690638c22e9b090604401610c65565b335f908152602081905260409020546001146127c95760405162461bcd60e51b8152602060048201526013602482015272105d5d1a0bdb9bdd0b585d5d1a1bdc9a5e9959606a1b6044820152606401610bdd565b816636b0b730b3b2b960c91b03612807576001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03831617905561285b565b60405162461bcd60e51b8152602060048201526024808201527f455243373534305661756c742f66696c652d756e7265636f676e697a65642d706044820152636172616d60e01b6064820152608401610bdd565b6040516001600160a01b038216815282907f8fef588b5fc1afbf5b2f06c1a435d513f208da2e6704c3d8f0e0ec91167066ba9060200160405180910390a25050565b6001546040516395b734fb60e01b81523060048201526001600160a01b0383811660248301525f9216906395b734fb90604401610c65565b5f6128df82612a2b565b600154604051633a4d2cd160e01b8152306004820152602481018690526001600160a01b038581166044830152848116606483015290911690633a4d2cd1906084016020604051808303815f875af115801561293d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061296191906131c5565b9050816001600160a01b0316836001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d783876040516110f1929190918252602082015260400190565b5f610ebe8261289d565b600154604051631b5fa0df60e21b81523060048201526001600160a01b0383811660248301525f921690636d7e837c90604401611998565b600154604051638ae5ecff60e01b81523060048201526001600160a01b0383811660248301525f921690638ae5ecff90604401610e7f565b6001600160a01b038116331480612a6457506001600160a01b0381165f90815260036020908152604080832033845290915290205460ff165b612ab05760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f696e76616c69642d636f6e74726f6c6c6572006044820152606401610bdd565b50565b6040516001600160a01b038381166024830152604482018390525f91829186169060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b17905251612b0c9190613323565b5f604051808303815f865af19150503d805f8114612b45576040519150601f19603f3d011682016040523d82523d5f602084013e612b4a565b606091505b5091509150818015612b74575080511580612b74575080806020019051810190612b7491906131dc565b612bcc5760405162461bcd60e51b8152602060048201526024808201527f536166655472616e736665724c69622f736166652d7472616e736665722d66616044820152631a5b195960e21b6064820152608401610bdd565b5050505050565b5f6001600160a01b038416612c2a5760405162461bcd60e51b815260206004820152601b60248201527f5369676e61747572654c69622f696e76616c69642d7369676e657200000000006044820152606401610bdd565b8151604103612cc25760208281015160408085015160608087015183515f8082529681018086528a9052951a928501839052840183905260808401819052919260019060a0016020604051602081039080840390855afa158015612c90573d5f803e3d5ffd5b505050602060405103516001600160a01b0316876001600160a01b031603612cbe5760019350505050610ebe565b5050505b6001600160a01b0384163b15610ebe575f80856001600160a01b03168585604051602401612cf1929190613339565b60408051601f198184030181529181526020820180516001600160e01b0316630b135d3f60e11b17905251612d269190613323565b5f60405180830381855afa9150503d805f8114612d5e576040519150601f19603f3d011682016040523d82523d5f602084013e612d63565b606091505b5091509150818015612d76575080516020145b8015612da757508051630b135d3f60e11b90612d9b9083016020908101908401613375565b6001600160e01b031916145b9695505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390525f91829187169060840160408051601f198184030181529181526020820180516001600160e01b03166323b872dd60e01b17905251612e129190613323565b5f604051808303815f865af19150503d805f8114612e4b576040519150601f19603f3d011682016040523d82523d5f602084013e612e50565b606091505b5091509150818015612e7a575080511580612e7a575080806020019051810190612e7a91906131dc565b612ed85760405162461bcd60e51b815260206004820152602960248201527f536166655472616e736665724c69622f736166652d7472616e736665722d66726044820152681bdb4b59985a5b195960ba1b6064820152608401610bdd565b505050505050565b80356001600160a01b0381168114612ef6575f80fd5b919050565b5f805f60608486031215612f0d575f80fd5b83359250612f1d60208501612ee0565b9150612f2b60408501612ee0565b90509250925092565b6001600160e01b031981168114612ab0575f80fd5b5f60208284031215612f59575f80fd5b8135610ebe81612f34565b5f805f60608486031215612f76575f80fd5b612f7f84612ee0565b95602085013595506040909401359392505050565b5f60208284031215612fa4575f80fd5b5035919050565b8015158114612ab0575f80fd5b5f8060408385031215612fc9575f80fd5b612fd283612ee0565b91506020830135612fe281612fab565b809150509250929050565b5f8060408385031215612ffe575f80fd5b8235915061300e60208401612ee0565b90509250929050565b5f805f60608486031215613029575f80fd5b61303284612ee0565b925061304060208501612ee0565b929592945050506040919091013590565b5f60208284031215613061575f80fd5b610ebe82612ee0565b5f806040838503121561307b575f80fd5b61308483612ee0565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b5f805f805f8060c087890312156130bb575f80fd5b6130c487612ee0565b95506130d260208801612ee0565b945060408701356130e281612fab565b9350606087013592506080870135915060a087013567ffffffffffffffff81111561310b575f80fd5b8701601f8101891361311b575f80fd5b803567ffffffffffffffff81111561313557613135613092565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561316457613164613092565b6040528181528282016020018b101561317b575f80fd5b816020840160208301375f602083830101528093505050509295509295509295565b5f80604083850312156131ae575f80fd5b6131b783612ee0565b915061300e60208401612ee0565b5f602082840312156131d5575f80fd5b5051919050565b5f602082840312156131ec575f80fd5b8151610ebe81612fab565b5f60208284031215613207575f80fd5b815167ffffffffffffffff81168114610ebe575f80fd5b634e487b7160e01b5f52601160045260245ffd5b6001815b600184111561326d578085048111156132515761325161321e565b600184161561325f57908102905b60019390931c928002613236565b935093915050565b5f8261328357506001610b87565b8161328f57505f610b87565b81600181146132a557600281146132af576132cb565b6001915050610b87565b60ff8411156132c0576132c061321e565b50506001821b610b87565b5060208310610133831016604e8410600b84101617156132ee575081810a610b87565b6132fa5f198484613232565b805f190482111561330d5761330d61321e565b029392505050565b5f610ebe60ff841683613275565b5f82518060208501845e5f920191825250919050565b828152604060208201525f82518060408401528060208501606085015e5f606082850101526060601f19601f8301168401019150509392505050565b5f60208284031215613385575f80fd5b8151610ebe81612f3456fea264697066735822122089174f1f9ecde22e821eb151ac7344d82667e3b8a1b6d76fdb2bfc48d7a2127164736f6c634300081a0033

Verified Source Code Full Match

Compiler: v0.8.26+commit.8a97fa7a EVM: cancun Optimization: Yes (500 runs)
Auth.sol 35 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

import {IAuth} from "src/interfaces/IAuth.sol";

/// @title  Auth
/// @notice Simple authentication pattern
/// @author Based on code from https://github.com/makerdao/dss
abstract contract Auth is IAuth {
    /// @inheritdoc IAuth
    mapping(address => uint256) public wards;

    constructor(address initialWard) {
        wards[initialWard] = 1;
        emit Rely(initialWard);
    }

    /// @dev Check if the msg.sender has permissions
    modifier auth() {
        require(wards[msg.sender] == 1, "Auth/not-authorized");
        _;
    }

    /// @inheritdoc IAuth
    function rely(address user) external auth {
        wards[user] = 1;
        emit Rely(user);
    }

    /// @inheritdoc IAuth
    function deny(address user) external auth {
        wards[user] = 0;
        emit Deny(user);
    }
}
ERC7540Vault.sol 431 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

import {Auth} from "src/Auth.sol";
import {IRoot} from "src/interfaces/IRoot.sol";
import {EIP712Lib} from "src/libraries/EIP712Lib.sol";
import {IRecoverable} from "src/interfaces/IRoot.sol";
import {ITranche} from "src/interfaces/token/ITranche.sol";
import {SignatureLib} from "src/libraries/SignatureLib.sol";
import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol";
import {IInvestmentManager} from "src/interfaces/IInvestmentManager.sol";
import "src/interfaces/IERC7540.sol";
import "src/interfaces/IERC7575.sol";
import "src/interfaces/IERC20.sol";

/// @title  ERC7540Vault
/// @notice Asynchronous Tokenized Vault standard implementation for Centrifuge pools
///
/// @dev    Each vault issues shares of Centrifuge tranches as restricted ERC-20 tokens
///         against asset deposits based on the current share price.
///
///         ERC-7540 is an extension of the ERC-4626 standard by 'requestDeposit' & 'requestRedeem' methods, where
///         deposit and redeem orders are submitted to the pools to be included in the execution of the following epoch.
///         After execution users can use the deposit, mint, redeem and withdraw functions to get their shares
///         and/or assets from the pools.
contract ERC7540Vault is Auth, IERC7540Vault {
    /// @dev Requests for Centrifuge pool are non-fungible and all have ID = 0
    uint256 private constant REQUEST_ID = 0;

    IRoot public immutable root;
    address public immutable escrow;
    IInvestmentManager public manager;

    /// @inheritdoc IERC7540Vault
    uint64 public immutable poolId;
    /// @inheritdoc IERC7540Vault
    bytes16 public immutable trancheId;

    /// @inheritdoc IERC7575
    address public immutable asset;

    /// @inheritdoc IERC7575
    address public immutable share;
    uint8 internal immutable _shareDecimals;

    /// --- ERC7741 ---
    bytes32 private immutable nameHash;
    bytes32 private immutable versionHash;
    uint256 public immutable deploymentChainId;
    bytes32 private immutable _DOMAIN_SEPARATOR;
    bytes32 public constant AUTHORIZE_OPERATOR_TYPEHASH =
        keccak256("AuthorizeOperator(address controller,address operator,bool approved,bytes32 nonce,uint256 deadline)");

    /// @inheritdoc IERC7741
    mapping(address controller => mapping(bytes32 nonce => bool used)) public authorizations;

    /// @inheritdoc IERC7540Operator
    mapping(address => mapping(address => bool)) public isOperator;

    // --- Events ---
    event File(bytes32 indexed what, address data);

    constructor(
        uint64 poolId_,
        bytes16 trancheId_,
        address asset_,
        address share_,
        address root_,
        address escrow_,
        address manager_
    ) Auth(msg.sender) {
        poolId = poolId_;
        trancheId = trancheId_;
        asset = asset_;
        share = share_;
        _shareDecimals = IERC20Metadata(share).decimals();
        root = IRoot(root_);
        escrow = escrow_;
        manager = IInvestmentManager(manager_);

        nameHash = keccak256(bytes("Centrifuge"));
        versionHash = keccak256(bytes("1"));
        deploymentChainId = block.chainid;
        _DOMAIN_SEPARATOR = EIP712Lib.calculateDomainSeparator(nameHash, versionHash);
    }

    // --- Administration ---
    function file(bytes32 what, address data) external auth {
        if (what == "manager") manager = IInvestmentManager(data);
        else revert("ERC7540Vault/file-unrecognized-param");
        emit File(what, data);
    }

    /// @inheritdoc IRecoverable
    function recoverTokens(address token, address to, uint256 amount) external auth {
        SafeTransferLib.safeTransfer(token, to, amount);
    }

    // --- ERC-7540 methods ---
    /// @inheritdoc IERC7540Deposit
    function requestDeposit(uint256 assets, address controller, address owner) public returns (uint256) {
        require(owner == msg.sender || isOperator[owner][msg.sender], "ERC7540Vault/invalid-owner");
        require(IERC20(asset).balanceOf(owner) >= assets, "ERC7540Vault/insufficient-balance");

        require(
            manager.requestDeposit(address(this), assets, controller, owner, msg.sender),
            "ERC7540Vault/request-deposit-failed"
        );
        SafeTransferLib.safeTransferFrom(asset, owner, address(escrow), assets);

        emit DepositRequest(controller, owner, REQUEST_ID, msg.sender, assets);
        return REQUEST_ID;
    }

    /// @inheritdoc IERC7540Deposit
    function pendingDepositRequest(uint256, address controller) public view returns (uint256 pendingAssets) {
        pendingAssets = manager.pendingDepositRequest(address(this), controller);
    }

    /// @inheritdoc IERC7540Deposit
    function claimableDepositRequest(uint256, address controller) external view returns (uint256 claimableAssets) {
        claimableAssets = maxDeposit(controller);
    }

    /// @inheritdoc IERC7540Redeem
    function requestRedeem(uint256 shares, address controller, address owner) public returns (uint256) {
        require(ITranche(share).balanceOf(owner) >= shares, "ERC7540Vault/insufficient-balance");

        // If msg.sender is operator of owner, the transfer is executed as if
        // the sender is the owner, to bypass the allowance check
        address sender = isOperator[owner][msg.sender] ? owner : msg.sender;

        require(
            manager.requestRedeem(address(this), shares, controller, owner, sender),
            "ERC7540Vault/request-redeem-failed"
        );

        require(
            ITranche(share).checkTransferRestriction(owner, address(escrow), shares), "ERC7540Vault/restrictions-failed"
        );

        try ITranche(share).authTransferFrom(sender, owner, address(escrow), shares) returns (bool) {}
        catch {
            // Support tranche tokens that block authTransferFrom. In this case ERC20 approval needs to be set
            require(ITranche(share).transferFrom(owner, address(escrow), shares), "ERC7540Vault/transfer-from-failed");
        }

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

    /// @inheritdoc IERC7540Redeem
    function pendingRedeemRequest(uint256, address controller) public view returns (uint256 pendingShares) {
        pendingShares = manager.pendingRedeemRequest(address(this), controller);
    }

    /// @inheritdoc IERC7540Redeem
    function claimableRedeemRequest(uint256, address controller) external view returns (uint256 claimableShares) {
        claimableShares = maxRedeem(controller);
    }

    // --- Asynchronous cancellation methods ---
    /// @inheritdoc IERC7540CancelDeposit
    function cancelDepositRequest(uint256, address controller) external {
        _validateController(controller);
        manager.cancelDepositRequest(address(this), controller, msg.sender);
        emit CancelDepositRequest(controller, REQUEST_ID, msg.sender);
    }

    /// @inheritdoc IERC7540CancelDeposit
    function pendingCancelDepositRequest(uint256, address controller) public view returns (bool isPending) {
        isPending = manager.pendingCancelDepositRequest(address(this), controller);
    }

    /// @inheritdoc IERC7540CancelDeposit
    function claimableCancelDepositRequest(uint256, address controller) public view returns (uint256 claimableAssets) {
        claimableAssets = manager.claimableCancelDepositRequest(address(this), controller);
    }

    /// @inheritdoc IERC7540CancelDeposit
    function claimCancelDepositRequest(uint256, address receiver, address controller)
        external
        returns (uint256 assets)
    {
        _validateController(controller);
        assets = manager.claimCancelDepositRequest(address(this), receiver, controller);
        emit CancelDepositClaim(receiver, controller, REQUEST_ID, msg.sender, assets);
    }

    /// @inheritdoc IERC7540CancelRedeem
    function cancelRedeemRequest(uint256, address controller) external {
        _validateController(controller);
        manager.cancelRedeemRequest(address(this), controller, msg.sender);
        emit CancelRedeemRequest(controller, REQUEST_ID, msg.sender);
    }

    /// @inheritdoc IERC7540CancelRedeem
    function pendingCancelRedeemRequest(uint256, address controller) public view returns (bool isPending) {
        isPending = manager.pendingCancelRedeemRequest(address(this), controller);
    }

    /// @inheritdoc IERC7540CancelRedeem
    function claimableCancelRedeemRequest(uint256, address controller) public view returns (uint256 claimableShares) {
        claimableShares = manager.claimableCancelRedeemRequest(address(this), controller);
    }

    /// @inheritdoc IERC7540CancelRedeem
    function claimCancelRedeemRequest(uint256, address receiver, address controller)
        external
        returns (uint256 shares)
    {
        _validateController(controller);
        shares = manager.claimCancelRedeemRequest(address(this), receiver, controller);
        emit CancelRedeemClaim(receiver, controller, REQUEST_ID, msg.sender, shares);
    }

    /// @inheritdoc IERC7540Operator
    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;
    }

    /// @inheritdoc IERC7540Vault
    function setEndorsedOperator(address owner, bool approved) public virtual {
        require(msg.sender != owner, "ERC7540Vault/cannot-set-self-as-operator");
        require(root.endorsed(msg.sender), "ERC7540Vault/not-endorsed");
        isOperator[owner][msg.sender] = approved;
        emit OperatorSet(owner, msg.sender, approved);
    }

    /// @inheritdoc IERC7741
    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        return block.chainid == deploymentChainId
            ? _DOMAIN_SEPARATOR
            : EIP712Lib.calculateDomainSeparator(nameHash, versionHash);
    }

    /// @inheritdoc IERC7741
    function authorizeOperator(
        address controller,
        address operator,
        bool approved,
        bytes32 nonce,
        uint256 deadline,
        bytes memory signature
    ) external 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 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR(),
                keccak256(abi.encode(AUTHORIZE_OPERATOR_TYPEHASH, controller, operator, approved, nonce, deadline))
            )
        );

        require(SignatureLib.isValidSignature(controller, digest, signature), "ERC7540Vault/invalid-authorization");

        isOperator[controller][operator] = approved;
        emit OperatorSet(controller, operator, approved);

        success = true;
    }

    /// @inheritdoc IERC7741
    function invalidateNonce(bytes32 nonce) external {
        authorizations[msg.sender][nonce] = true;
    }

    // --- ERC165 support ---
    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return interfaceId == type(IERC7540Deposit).interfaceId || interfaceId == type(IERC7540Redeem).interfaceId
            || interfaceId == type(IERC7540Operator).interfaceId || interfaceId == type(IERC7540CancelDeposit).interfaceId
            || interfaceId == type(IERC7540CancelRedeem).interfaceId || interfaceId == type(IERC7575).interfaceId
            || interfaceId == type(IERC7741).interfaceId || interfaceId == type(IERC7714).interfaceId
            || interfaceId == type(IERC165).interfaceId;
    }

    // --- ERC-4626 methods ---
    /// @inheritdoc IERC7575
    function totalAssets() external view returns (uint256) {
        return convertToAssets(IERC20Metadata(share).totalSupply());
    }

    /// @inheritdoc IERC7575
    /// @notice     The calculation is based on the token price from the most recent epoch retrieved from Centrifuge.
    ///             The actual conversion MAY change between order submission and execution.
    function convertToShares(uint256 assets) public view returns (uint256 shares) {
        shares = manager.convertToShares(address(this), assets);
    }

    /// @inheritdoc IERC7575
    /// @notice     The calculation is based on the token price from the most recent epoch retrieved from Centrifuge.
    ///             The actual conversion MAY change between order submission and execution.
    function convertToAssets(uint256 shares) public view returns (uint256 assets) {
        assets = manager.convertToAssets(address(this), shares);
    }

    /// @inheritdoc IERC7575
    function maxDeposit(address controller) public view returns (uint256 maxAssets) {
        maxAssets = manager.maxDeposit(address(this), controller);
    }

    /// @inheritdoc IERC7540Deposit
    function deposit(uint256 assets, address receiver, address controller) public returns (uint256 shares) {
        _validateController(controller);
        shares = manager.deposit(address(this), assets, receiver, controller);
        emit Deposit(receiver, controller, assets, shares);
    }

    /// @inheritdoc IERC7575
    /// @notice     When claiming deposit requests using deposit(), there can be some precision loss leading to dust.
    ///             It is recommended to use mint() to claim deposit requests instead.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares) {
        shares = deposit(assets, receiver, msg.sender);
    }

    /// @inheritdoc IERC7575
    function maxMint(address controller) public view returns (uint256 maxShares) {
        maxShares = manager.maxMint(address(this), controller);
    }

    /// @inheritdoc IERC7540Deposit
    function mint(uint256 shares, address receiver, address controller) public returns (uint256 assets) {
        _validateController(controller);
        assets = manager.mint(address(this), shares, receiver, controller);
        emit Deposit(receiver, controller, assets, shares);
    }

    /// @inheritdoc IERC7575
    function mint(uint256 shares, address receiver) public returns (uint256 assets) {
        assets = mint(shares, receiver, msg.sender);
    }

    /// @inheritdoc IERC7575
    function maxWithdraw(address controller) public view returns (uint256 maxAssets) {
        maxAssets = manager.maxWithdraw(address(this), controller);
    }

    /// @inheritdoc IERC7575
    /// @notice     DOES NOT support controller != msg.sender since shares are already transferred on requestRedeem
    function withdraw(uint256 assets, address receiver, address controller) public returns (uint256 shares) {
        _validateController(controller);
        shares = manager.withdraw(address(this), assets, receiver, controller);
        emit Withdraw(msg.sender, receiver, controller, assets, shares);
    }

    /// @inheritdoc IERC7575
    function maxRedeem(address controller) public view returns (uint256 maxShares) {
        maxShares = manager.maxRedeem(address(this), controller);
    }

    /// @inheritdoc IERC7575
    /// @notice     DOES NOT support controller != msg.sender since shares are already transferred on requestRedeem.
    ///             When claiming redemption requests using redeem(), there can be some precision loss leading to dust.
    ///             It is recommended to use withdraw() to claim redemption requests instead.
    function redeem(uint256 shares, address receiver, address controller) external returns (uint256 assets) {
        _validateController(controller);
        assets = manager.redeem(address(this), shares, receiver, controller);
        emit Withdraw(msg.sender, receiver, controller, assets, shares);
    }

    /// @dev Preview functions for ERC-7540 vaults revert
    function previewDeposit(uint256) external pure returns (uint256) {
        revert();
    }

    /// @dev Preview functions for ERC-7540 vaults revert
    function previewMint(uint256) external pure returns (uint256) {
        revert();
    }

    /// @dev Preview functions for ERC-7540 vaults revert
    function previewWithdraw(uint256) external pure returns (uint256) {
        revert();
    }

    /// @dev Preview functions for ERC-7540 vaults revert
    function previewRedeem(uint256) external pure returns (uint256) {
        revert();
    }

    // --- Event emitters ---
    function onRedeemRequest(address controller, address owner, uint256 shares) public auth {
        emit RedeemRequest(controller, owner, REQUEST_ID, msg.sender, shares);
    }

    function onDepositClaimable(address controller, uint256 assets, uint256 shares) public auth {
        emit DepositClaimable(controller, REQUEST_ID, assets, shares);
    }

    function onRedeemClaimable(address controller, uint256 assets, uint256 shares) public auth {
        emit RedeemClaimable(controller, REQUEST_ID, assets, shares);
    }

    function onCancelDepositClaimable(address controller, uint256 assets) public auth {
        emit CancelDepositClaimable(controller, REQUEST_ID, assets);
    }

    function onCancelRedeemClaimable(address controller, uint256 shares) public auth {
        emit CancelRedeemClaimable(controller, REQUEST_ID, shares);
    }

    // --- Helpers ---
    /// @notice Price of 1 unit of share, quoted in the decimals of the asset.
    function pricePerShare() external view returns (uint256) {
        return convertToAssets(10 ** _shareDecimals);
    }

    /// @notice Returns timestamp of the last share price update.
    function priceLastUpdated() external view returns (uint64) {
        return manager.priceLastUpdated(address(this));
    }

    /// @inheritdoc IERC7714
    function isPermissioned(address controller) external view returns (bool) {
        return ITranche(share).checkTransferRestriction(address(0), controller, 0);
    }

    /// @notice Ensures msg.sender can operate on behalf of controller.
    function _validateController(address controller) internal view {
        require(controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-controller");
    }
}
IAuth.sol 16 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

interface IAuth {
    event Rely(address indexed user);
    event Deny(address indexed user);

    /// @notice Returns whether the target is a ward (has admin access)
    function wards(address target) external view returns (uint256);

    /// @notice Make user a ward (give them admin access)
    function rely(address user) external;

    /// @notice Remove user as a ward (remove admin access)
    function deny(address user) external;
}
IRoot.sol 90 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

import {IMessageHandler} from "src/interfaces/gateway/IGateway.sol";

interface IRecoverable {
    /// @notice Used to recover any ERC-20 token.
    /// @dev    This method is called only by authorized entities
    /// @param  token It could be 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
    ///         to recover locked native ETH or any ERC20 compatible token.
    /// @param  to Receiver of the funds
    /// @param  amount Amount to send to the receiver.
    function recoverTokens(address token, address to, uint256 amount) external;
}

interface IRoot is IMessageHandler {
    // --- Events ---
    event File(bytes32 indexed what, uint256 data);
    event Pause();
    event Unpause();
    event ScheduleRely(address indexed target, uint256 indexed scheduledTime);
    event CancelRely(address indexed target);
    event RelyContract(address indexed target, address indexed user);
    event DenyContract(address indexed target, address indexed user);
    event RecoverTokens(address indexed target, address indexed token, address indexed to, uint256 amount);
    event Endorse(address indexed user);
    event Veto(address indexed user);

    /// @notice Returns whether the root is paused
    function paused() external view returns (bool);

    /// @notice Returns the current timelock for adding new wards
    function delay() external view returns (uint256);

    /// @notice Trusted contracts within the system
    function endorsements(address target) external view returns (uint256);

    /// @notice Returns when `relyTarget` has passed the timelock
    function schedule(address relyTarget) external view returns (uint256 timestamp);

    // --- Administration ---
    /// @notice Updates a contract parameter
    /// @param what Accepts a bytes32 representation of 'delay'
    function file(bytes32 what, uint256 data) external;

    /// --- Endorsements ---
    /// @notice Endorses the `user`
    /// @dev    Endorsed users are trusted contracts in the system. They are allowed to bypass
    ///         token restrictions (e.g. the Escrow can automatically receive tranche tokens by being endorsed), and
    ///         can automatically set operators in ERC-7540 vaults (e.g. the CentrifugeRouter) is always an operator.
    function endorse(address user) external;

    /// @notice Removes the endorsed user
    function veto(address user) external;

    /// @notice Returns whether the user is endorsed
    function endorsed(address user) external view returns (bool);

    // --- Pause management ---
    /// @notice Pause any contracts that depend on `Root.paused()`
    function pause() external;

    /// @notice Unpause any contracts that depend on `Root.paused()`
    function unpause() external;

    /// --- Timelocked ward management ---
    /// @notice Schedule relying a new ward after the delay has passed
    function scheduleRely(address target) external;

    /// @notice Cancel a pending scheduled rely
    function cancelRely(address target) external;

    /// @notice Execute a scheduled rely
    /// @dev    Can be triggered by anyone since the scheduling is protected
    function executeScheduledRely(address target) external;

    /// --- Incoming message handling ---
    function handle(bytes calldata message) external;

    /// --- External contract ward management ---
    /// @notice Make an address a ward on any contract that Root is a ward on
    function relyContract(address target, address user) external;

    /// @notice Removes an address as a ward on any contract that Root is a ward on
    function denyContract(address target, address user) external;

    /// --- Token Recovery ---
    /// @notice Allows Governance to recover tokens sent to the wrong contract by mistake
    function recoverTokens(address target, address token, address to, uint256 amount) external;
}
IERC20.sol 193 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

/// @title  IERC20
/// @dev    Interface of the ERC20 standard as defined in the EIP.
/// @author Modified from OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

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

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

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

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

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

interface IERC20Wrapper {
    /**
     * @dev Returns the address of the underlying ERC-20 token that is being wrapped.
     */
    function underlying() external view returns (address);

    /**
     * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens.
     */
    function depositFor(address account, uint256 value) external returns (bool);

    /**
     * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens.
     */
    function withdrawTo(address account, uint256 value) external returns (bool);
}
IERC7540.sol 325 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

import {IERC7575} from "src/interfaces/IERC7575.sol";
import {IRecoverable} from "src/interfaces/IRoot.sol";

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 is IERC7540Operator {
    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 is IERC7540Operator {
    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);
}

interface IERC7540CancelDeposit {
    event CancelDepositRequest(address indexed controller, uint256 indexed requestId, address sender);
    event CancelDepositClaim(
        address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 assets
    );

    /**
     * @dev Submits a Request for cancelling the pending deposit Request
     *
     * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
     *    approval of ERC-20 tokens from controller to sender is NOT enough.
     * - MUST set pendingCancelDepositRequest to `true` for the returned requestId after request
     * - MUST increase claimableCancelDepositRequest for the returned requestId after fulfillment
     * - SHOULD be claimable using `claimCancelDepositRequest`
     * Note: while `pendingCancelDepositRequest` is `true`, `requestDeposit` cannot be called
     */
    function cancelDepositRequest(uint256 requestId, address controller) external;

    /**
     * @dev Returns whether the deposit Request is pending cancelation
     *
     * - MUST NOT show any variations depending on the caller.
     */
    function pendingCancelDepositRequest(uint256 requestId, address controller)
        external
        view
        returns (bool isPending);

    /**
     * @dev Returns the amount of assets that were canceled from a deposit Request, and can now be claimed.
     *
     * - MUST NOT show any variations depending on the caller.
     */
    function claimableCancelDepositRequest(uint256 requestId, address controller)
        external
        view
        returns (uint256 claimableAssets);

    /**
     * @dev Claims the canceled deposit assets, and removes the pending cancelation Request
     *
     * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
     *    approval of ERC-20 tokens from controller to sender is NOT enough.
     * - MUST set pendingCancelDepositRequest to `false` for the returned requestId after request
     * - MUST set claimableCancelDepositRequest to 0 for the returned requestId after fulfillment
     */
    function claimCancelDepositRequest(uint256 requestId, address receiver, address controller)
        external
        returns (uint256 assets);
}

interface IERC7540CancelRedeem {
    event CancelRedeemRequest(address indexed controller, uint256 indexed requestId, address sender);
    event CancelRedeemClaim(
        address indexed receiver, address indexed controller, uint256 indexed requestId, address sender, uint256 shares
    );

    /**
     * @dev Submits a Request for cancelling the pending redeem Request
     *
     * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
     *    approval of ERC-20 tokens from controller to sender is NOT enough.
     * - MUST set pendingCancelRedeemRequest to `true` for the returned requestId after request
     * - MUST increase claimableCancelRedeemRequest for the returned requestId after fulfillment
     * - SHOULD be claimable using `claimCancelRedeemRequest`
     * Note: while `pendingCancelRedeemRequest` is `true`, `requestRedeem` cannot be called
     */
    function cancelRedeemRequest(uint256 requestId, address controller) external;

    /**
     * @dev Returns whether the redeem Request is pending cancelation
     *
     * - MUST NOT show any variations depending on the caller.
     */
    function pendingCancelRedeemRequest(uint256 requestId, address controller) external view returns (bool isPending);

    /**
     * @dev Returns the amount of shares that were canceled from a redeem Request, and can now be claimed.
     *
     * - MUST NOT show any variations depending on the caller.
     */
    function claimableCancelRedeemRequest(uint256 requestId, address controller)
        external
        view
        returns (uint256 claimableShares);

    /**
     * @dev Claims the canceled redeem shares, and removes the pending cancelation Request
     *
     * - controller MUST be msg.sender unless some unspecified explicit approval is given by the caller,
     *    approval of ERC-20 tokens from controller to sender is NOT enough.
     * - MUST set pendingCancelRedeemRequest to `false` for the returned requestId after request
     * - MUST set claimableCancelRedeemRequest to 0 for the returned requestId after fulfillment
     */
    function claimCancelRedeemRequest(uint256 requestId, address receiver, address controller)
        external
        returns (uint256 shares);
}

interface IERC7741 {
    /**
     * @dev Grants or revokes permissions for `operator` to manage Requests on behalf of the
     *      `msg.sender`, using an [EIP-712](./eip-712.md) signature.
     */
    function authorizeOperator(
        address controller,
        address operator,
        bool approved,
        bytes32 nonce,
        uint256 deadline,
        bytes memory signature
    ) external returns (bool);

    /**
     * @dev Revokes the given `nonce` for `msg.sender` as the `owner`.
     */
    function invalidateNonce(bytes32 nonce) external;

    /**
     * @dev Returns whether the given `nonce` has been used for the `controller`.
     */
    function authorizations(address controller, bytes32 nonce) external view returns (bool used);

    /**
     * @dev Returns the `DOMAIN_SEPARATOR` as defined according to EIP-712. The `DOMAIN_SEPARATOR
     *      should be unique to the contract and chain to prevent replay attacks from other domains,
     *      and satisfy the requirements of EIP-712, but is otherwise unconstrained.
     */
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

interface IERC7714 {
    /**
     * @dev Returns `true` if the `user` is permissioned to interact with the contract.
     */
    function isPermissioned(address controller) external view returns (bool);
}

/**
 * @title  IERC7540Vault
 * @dev    This is the specific set of interfaces used by the Centrifuge impelmentation of ERC7540,
 *         as a fully asynchronous Vault, with cancelation support, and authorize operator signature support.
 */
interface IERC7540Vault is
    IERC7540Deposit,
    IERC7540Redeem,
    IERC7540CancelDeposit,
    IERC7540CancelRedeem,
    IERC7575,
    IERC7741,
    IERC7714,
    IRecoverable
{
    event DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
    event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
    event CancelDepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets);
    event CancelRedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 shares);

    /// @notice Identifier of the Centrifuge pool
    function poolId() external view returns (uint64);

    /// @notice Identifier of the tranche of the Centrifuge pool
    function trancheId() external view returns (bytes16);

    /// @notice Set msg.sender as operator of owner, to `approved` status
    /// @dev    MUST be called by endorsed sender
    function setEndorsedOperator(address owner, bool approved) external;

    /// @notice Callback when a redeem Request is triggered externally;
    function onRedeemRequest(address controller, address owner, uint256 shares) external;

    /// @notice Callback when a deposit Request becomes claimable
    function onDepositClaimable(address owner, uint256 assets, uint256 shares) external;

    /// @notice Callback when a redeem Request becomes claimable
    function onRedeemClaimable(address owner, uint256 assets, uint256 shares) external;

    /// @notice Callback when a claim deposit Request becomes claimable
    function onCancelDepositClaimable(address owner, uint256 assets) external;

    /// @notice Callback when a claim redeem Request becomes claimable
    function onCancelRedeemClaimable(address owner, uint256 shares) external;
}
IERC7575.sol 254 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);
}

interface IERC7575Share is IERC165 {
    event VaultUpdate(address indexed asset, address vault);

    /**
     * @dev Returns the address of the Vault for the given asset.
     *
     * @param asset the ERC-20 token to deposit with into the Vault
     */
    function vault(address asset) external view returns (address);
}
EIP712Lib.sol 12 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

/// @title  EIP712 Lib
library EIP712Lib {
    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    function calculateDomainSeparator(bytes32 nameHash, bytes32 versionHash) internal view returns (bytes32) {
        return keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, nameHash, versionHash, block.chainid, address(this)));
    }
}
SignatureLib.sol 38 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

interface IERC1271 {
    function isValidSignature(bytes32, bytes memory) external view returns (bytes4);
}

/// @title  Signature Lib
library SignatureLib {
    function isValidSignature(address signer, bytes32 digest, bytes memory signature)
        internal
        view
        returns (bool valid)
    {
        require(signer != address(0), "SignatureLib/invalid-signer");

        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            if (signer == ecrecover(digest, v, r, s)) {
                return true;
            }
        }

        if (signer.code.length > 0) {
            (bool success, bytes memory result) =
                signer.staticcall(abi.encodeCall(IERC1271.isValidSignature, (digest, signature)));
            valid =
                (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector);
        }
    }
}
ITranche.sol 72 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

import {IERC20Metadata} from "src/interfaces/IERC20.sol";
import {IERC7575Share} from "src/interfaces/IERC7575.sol";

interface IERC1404 {
    /// @notice Detects if a transfer will be reverted and if so returns an appropriate reference code
    /// @param from Sending address
    /// @param to Receiving address
    /// @param value Amount of tokens being transferred
    /// @return Code by which to reference message for rejection reasoning
    /// @dev Overwrite with your custom transfer restriction logic
    function detectTransferRestriction(address from, address to, uint256 value) external view returns (uint8);

    /// @notice Returns a human-readable message for a given restriction code
    /// @param restrictionCode Identifier for looking up a message
    /// @return Text showing the restriction's reasoning
    /// @dev Overwrite with your custom message and restrictionCode handling
    function messageForTransferRestriction(uint8 restrictionCode) external view returns (string memory);
}

interface ITranche is IERC20Metadata, IERC7575Share, IERC1404 {
    // --- Events ---
    event File(bytes32 indexed what, address data);
    event SetHookData(address indexed user, bytes16 data);

    struct Balance {
        /// @dev The user balance is limited to uint128. This is safe because the decimals are limited to 18,
        ///      thus the max balance is 2^128-1 / 10**18 = 3.40 * 10**20. This is also enforced on mint.
        uint128 amount;
        /// @dev There are 16 bytes that are used to store hook data (e.g. restrictions for users).
        bytes16 hookData;
    }

    // --- Administration ---
    /// @notice returns the hook that transfers perform callbacks to
    /// @dev    MUST comply to `IHook` interface
    function hook() external view returns (address);

    /// @notice Updates a contract parameter
    /// @param what Accepts a bytes32 representation of 'name', 'symbol'
    function file(bytes32 what, string memory data) external;

    /// @notice Updates a contract parameter
    /// @param what Accepts a bytes32 representation of 'hook'
    function file(bytes32 what, address data) external;

    /// @notice updates the vault for a given `asset`
    function updateVault(address asset, address vault_) external;

    // --- ERC20 overrides ---
    /// @notice returns the 16 byte hook data of the given `user`.
    /// @dev    Stored in the 128 most significant bits of the user balance
    function hookDataOf(address user) external view returns (bytes16);

    /// @notice update the 16 byte hook data of the given `user`
    function setHookData(address user, bytes16 hookData) external;

    /// @notice Function to mint tokens
    function mint(address user, uint256 value) external;

    /// @notice Function to burn tokens
    function burn(address user, uint256 value) external;

    /// @notice Checks if the tokens can be transferred given the input values
    function checkTransferRestriction(address from, address to, uint256 value) external view returns (bool);

    /// @notice Performs an authorized transfer, with `sender` as the given sender.
    /// @dev    Requires allowance if `sender` != `from`
    function authTransferFrom(address sender, address from, address to, uint256 amount) external returns (bool);
}
SafeTransferLib.sol 49 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

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

/// @title  Safe Transfer Lib
/// @author Modified from Uniswap v3 Periphery (libraries/TransferHelper.sol)
library SafeTransferLib {
    /// @notice Transfers tokens from the targeted address to the given destination
    /// @notice Errors if transfer fails
    /// @param token The contract address of the token to be transferred
    /// @param from The originating address from which the tokens will be transferred
    /// @param to The destination address of the transfer
    /// @param value The amount to be transferred
    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeCall(IERC20.transferFrom, (from, to, value)));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-transfer-from-failed");
    }

    /// @notice Transfers tokens from msg.sender to a recipient
    /// @dev Errors if transfer fails
    /// @param token The contract address of the token which will be transferred
    /// @param to The recipient of the transfer
    /// @param value The value of the transfer
    function safeTransfer(address token, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeCall(IERC20.transfer, (to, value)));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-transfer-failed");
    }

    /// @notice Approves the stipulated contract to spend the given allowance in the given token
    /// @dev Errors if approval fails
    /// @param token The contract address of the token to be approved
    /// @param to The target of the approval
    /// @param value The amount of the given token the target will be allowed to spend
    function safeApprove(address token, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeCall(IERC20.approve, (to, value)));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-approve-failed");
    }

    /// @notice Transfers ETH to the recipient address
    /// @dev Fails with `STE`
    /// @dev Make sure that method that is using this function is protected from reentrancy
    /// @param to The destination of the transfer
    /// @param value The value to be transferred
    function safeTransferETH(address to, uint256 value) internal {
        (bool success,) = to.call{value: value}(new bytes(0));
        require(success, "SafeTransferLib/safe-transfer-eth-failed");
    }
}
IGateway.sol 170 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

uint8 constant MAX_ADAPTER_COUNT = 8;

interface IGateway {
    /// @dev Each adapter struct is packed with the quorum to reduce SLOADs on handle
    struct Adapter {
        /// @notice Starts at 1 and maps to id - 1 as the index on the adapters array
        uint8 id;
        /// @notice Number of votes required for a message to be executed
        uint8 quorum;
        /// @notice Each time the quorum is decreased, a new session starts which invalidates old votes
        uint64 activeSessionId;
    }

    struct Message {
        /// @dev Counts are stored as integers (instead of boolean values) to accommodate duplicate
        ///      messages (e.g. two investments from the same user with the same amount) being
        ///      processed in parallel. The entire struct is packed in a single bytes32 slot.
        ///      Max uint16 = 65,535 so at most 65,535 duplicate messages can be processed in parallel.
        uint16[MAX_ADAPTER_COUNT] votes;
        /// @notice Each time adapters are updated, a new session starts which invalidates old votes
        uint64 sessionId;
        bytes pendingMessage;
    }

    // --- Events ---
    event ProcessMessage(bytes message, address adapter);
    event ProcessProof(bytes32 messageHash, address adapter);
    event ExecuteMessage(bytes message, address adapter);
    event SendMessage(bytes message);
    event RecoverMessage(address adapter, bytes message);
    event RecoverProof(address adapter, bytes32 messageHash);
    event InitiateMessageRecovery(bytes32 messageHash, address adapter);
    event DisputeMessageRecovery(bytes32 messageHash, address adapter);
    event ExecuteMessageRecovery(bytes message, address adapter);
    event File(bytes32 indexed what, address[] adapters);
    event File(bytes32 indexed what, address instance);
    event File(bytes32 indexed what, uint8 messageId, address manager);
    event File(bytes32 indexed what, address caller, bool isAllowed);
    event ReceiveNativeTokens(address indexed sender, uint256 amount);

    /// @notice Returns the address of the adapter at the given id.
    function adapters(uint256 id) external view returns (address);

    /// @notice Returns the address of the contract that handles the given message id.
    function messageHandlers(uint8 messageId) external view returns (address);

    /// @notice Returns the timestamp when the given recovery can be executed.
    function recoveries(address adapter, bytes32 messageHash) external view returns (uint256 timestamp);

    // --- Administration ---
    /// @notice Used to update an array of addresses ( state variable ) on very rare occasions.
    /// @dev    Currently it is used to update the supported adapters.
    /// @param  what The name of the variable to be updated.
    /// @param  value New addresses.
    function file(bytes32 what, address[] calldata value) external;

    /// @notice Used to update an address ( state variable ) on very rare occasions.
    /// @dev    Currently used to update addresses of contract instances.
    /// @param  what The name of the variable to be updated.
    /// @param  data New address.
    function file(bytes32 what, address data) external;

    /// @notice Used to update a mapping ( state variables ) on very rare occasions.
    /// @dev    Currently used to update any custom handlers for a specific message type.
    ///         data1 is the message id from MessagesLib.Call and data2 could be any
    ///         custom instance of a contract that will handle that call.
    /// @param  what The name of the variable to be updated.
    /// @param  data1 The key of the mapping.
    /// @param  data2 The value of the mapping
    function file(bytes32 what, uint8 data1, address data2) external;

    /// @notice Used to update a mapping ( state variables ) on very rare occasions.
    /// @dev    Manages who is allowed to call `this.topUp`
    ///
    /// @param what The name of the variable to be updated - `payers`
    /// @param caller Address of the payer allowed to top-up
    /// @param isAllower Whether the `caller` is allowed to top-up or not
    function file(bytes32 what, address caller, bool isAllower) external;

    // --- Incoming ---
    /// @notice Handles incoming messages, proofs, and recoveries.
    /// @dev    Assumes adapters ensure messages cannot be confirmed more than once.
    /// @param  payload Incoming message from the Centrifuge Chain passed through adapters.
    function handle(bytes calldata payload) external;

    /// @notice Governance on Centrifuge Chain can initiate message recovery. After the challenge period,
    ///         the recovery can be executed. If a malign adapter initiates message recovery, governance on
    ///         Centrifuge Chain can dispute and immediately cancel the recovery, using any other valid adapter.
    /// @param  adapter Adapter that the recovery was targeting
    /// @param  messageHash Hash of the message being disputed
    function disputeMessageRecovery(address adapter, bytes32 messageHash) external;

    /// @notice Governance on Centrifuge Chain can initiate message recovery. After the challenge period,
    ///         the recovery can be executed. If a malign adapter initiates message recovery, governance on
    ///         Centrifuge Chain can dispute and immediately cancel the recovery, using any other valid adapter.
    ///
    ///         Only 1 recovery can be outstanding per message hash. If multiple adapters fail at the same time,
    ///         these will need to be recovered serially (increasing the challenge period for each failed adapter).
    /// @param  adapter Adapter's address that the recovery is targeting
    /// @param  message Hash of the message to be recovered
    function executeMessageRecovery(address adapter, bytes calldata message) external;

    // --- Outgoing ---
    /// @notice Sends outgoing messages to the Centrifuge Chain.
    /// @dev    Sends 1 message to the first adapter with the full message,
    ///         and n-1 messages to the other adapters with proofs (hash of message).
    ///         This ensures message uniqueness (can only be executed on the destination once).
    ///         Source could be either Centrifuge router or EoA or any contract
    ///         that calls the ERC7540Vault contract directly.
    /// @param  message Message to be send. Either the message itself or a hash value of it ( proof ).
    /// @param  source Entry point of the transaction.
    ///         Used to determine whether it is eligible for TX cost payment.
    function send(bytes calldata message, address source) external payable;

    /// @notice Prepays for the TX cost for sending through the adapters
    ///         and Centrifuge Chain
    /// @dev    It can be called only through endorsed contracts.
    ///         Currently being called from Centrifuge Router only.
    ///         In order to prepay, the method MUST be called with `msg.value`.
    ///         Called is assumed to have called IGateway.estimate before calling this.
    function topUp() external payable;

    // --- Helpers ---
    /// @notice A view method of the current quorum.abi
    /// @dev    Quorum shows the amount of votes needed in order for a message to be dispatched further.
    ///         The quorum is taken from the first adapter.
    ///         Current quorum is the amount of all adapters.
    /// return  Needed amount
    function quorum() external view returns (uint8);

    /// @notice Gets the current active routers session id.
    /// @dev    When the adapters are updated with new ones,
    ///         each new set of adapters has their own sessionId.
    ///         Currently it uses sessionId of the previous set and
    ///         increments it by 1. The idea of an activeSessionId is
    ///         to invalidate any incoming messages from previously used adapters.
    function activeSessionId() external view returns (uint64);

    /// @notice Counts how many times each incoming messages has been received per adapter.
    /// @dev    It supports parallel messages ( duplicates ). That means that the incoming messages could be
    ///         the result of two or more independ request from the user of the same type.
    ///         i.e. Same user would like to deposit same underlying asset with the same amount more then once.
    /// @param  messageHash The hash value of the incoming message.
    function votes(bytes32 messageHash) external view returns (uint16[MAX_ADAPTER_COUNT] memory);

    /// @notice Used to calculate overall cost for bridging a payload on the first adapter and settling
    ///         on the destination chain and bridging its payload proofs on n-1 adapter
    ///         and settling on the destination chain.
    /// @param  payload Used in gas cost calculations.
    /// @dev    Currenly the payload is not taken into consideration.
    /// @return perAdapter An array of cost values per adapter. Each value is how much it's going to cost
    ///         for a message / proof to be passed through one router and executed on Centrifuge Chain
    /// @return total Total cost for sending one message and corresponding proofs on through all adapters
    function estimate(bytes calldata payload) external view returns (uint256[] memory perAdapter, uint256 total);

    /// @notice Used to check current state of the `caller` and whether they are allowed to call
    ///         `this.topUp` or not.
    /// @param  caller Address to check
    /// @return isAllowed Whether the `caller` `isAllowed to call `this.topUp()`
    function payers(address caller) external view returns (bool isAllowed);
}

interface IMessageHandler {
    /// @notice Handling incoming messages from Centrifuge Chain.
    /// @param  message Incoming message
    function handle(bytes memory message) external;
}
IInvestmentManager.sol 329 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.5.0;

import {IMessageHandler} from "src/interfaces/gateway/IGateway.sol";
import {IRecoverable} from "src/interfaces/IRoot.sol";

/// @dev Vault requests and deposit/redeem bookkeeping per user
struct InvestmentState {
    /// @dev Shares that can be claimed using `mint()`
    uint128 maxMint;
    /// @dev Assets that can be claimed using `withdraw()`
    uint128 maxWithdraw;
    /// @dev Weighted average price of deposits, used to convert maxMint to maxDeposit
    uint256 depositPrice;
    /// @dev Weighted average price of redemptions, used to convert maxWithdraw to maxRedeem
    uint256 redeemPrice;
    /// @dev Remaining deposit request in assets
    uint128 pendingDepositRequest;
    /// @dev Remaining redeem request in shares
    uint128 pendingRedeemRequest;
    /// @dev Assets that can be claimed using `claimCancelDepositRequest()`
    uint128 claimableCancelDepositRequest;
    /// @dev Shares that can be claimed using `claimCancelRedeemRequest()`
    uint128 claimableCancelRedeemRequest;
    /// @dev Indicates whether the depositRequest was requested to be cancelled
    bool pendingCancelDepositRequest;
    /// @dev Indicates whether the redeemRequest was requested to be cancelled
    bool pendingCancelRedeemRequest;
}

interface IInvestmentManager is IMessageHandler, IRecoverable {
    // --- Events ---
    event File(bytes32 indexed what, address data);
    event TriggerRedeemRequest(
        uint64 indexed poolId, bytes16 indexed trancheId, address user, address asset, uint128 shares
    );

    /// @notice Returns the investment state
    function investments(address vault, address investor)
        external
        view
        returns (
            uint128 maxMint,
            uint128 maxWithdraw,
            uint256 depositPrice,
            uint256 redeemPrice,
            uint128 pendingDepositRequest,
            uint128 pendingRedeemRequest,
            uint128 claimableCancelDepositRequest,
            uint128 claimableCancelRedeemRequest,
            bool pendingCancelDepositRequest,
            bool pendingCancelRedeemRequest
        );

    /// @notice Updates contract parameters of type address.
    /// @param what The bytes32 representation of 'gateway' or 'poolManager'.
    /// @param data The new contract address.
    function file(bytes32 what, address data) external;

    // --- Outgoing message handling ---
    /// @notice Requests assets deposit. Liquidity pools have to request investments from Centrifuge before
    ///         shares can be minted. The deposit requests are added to the order book
    ///         on Centrifuge. Once the next epoch is executed on Centrifuge, vaults can
    ///         proceed with share payouts in case the order got fulfilled.
    /// @dev    The assets required to fulfill the deposit request have to be locked and are transferred from the
    ///         owner to the escrow, even though the share payout can only happen after epoch execution.
    ///         The receiver becomes the owner of deposit request fulfillment.
    function requestDeposit(address vault, uint256 assets, address receiver, address owner, address source)
        external
        returns (bool);

    /// @notice Requests share redemption. Liquidity pools have to request redemptions
    ///         from Centrifuge before actual asset payouts can be done. The redemption
    ///         requests are added to the order book on Centrifuge. Once the next epoch is
    ///         executed on Centrifuge, vaults can proceed with asset payouts
    ///         in case the order got fulfilled.
    /// @dev    The shares required to fulfill the redemption request have to be locked and are transferred from the
    ///         owner to the escrow, even though the asset payout can only happen after epoch execution.
    ///         The receiver becomes the owner of redeem request fulfillment.
    function requestRedeem(address vault, uint256 shares, address receiver, address, /* owner */ address source)
        external
        returns (bool);

    /// @notice Requests the cancellation of a pending deposit request. Liquidity pools have to request the
    ///         cancellation of outstanding requests from Centrifuge before actual assets can be unlocked and
    /// transferred
    ///         to the owner.
    ///         While users have outstanding cancellation requests no new deposit requests can be submitted.
    ///         Once the next epoch is executed on Centrifuge, vaults can proceed with asset payouts
    ///         if orders could be cancelled successfully.
    /// @dev    The cancellation request might fail in case the pending deposit order already got fulfilled on
    ///         Centrifuge.
    function cancelDepositRequest(address vault, address owner, address source) external;

    /// @notice Requests the cancellation of an pending redeem request. Liquidity pools have to request the
    ///         cancellation of outstanding requests from Centrifuge before actual shares can be unlocked and
    ///         transferred to the owner.
    ///         While users have outstanding cancellation requests no new redeem requests can be submitted (exception:
    ///         trigger through governance).
    ///         Once the next epoch is executed on Centrifuge, vaults can proceed with share payouts
    ///         if the orders could be cancelled successfully.
    /// @dev    The cancellation request might fail in case the pending redeem order already got fulfilled on
    ///         Centrifuge.
    function cancelRedeemRequest(address vault, address owner, address source) external;

    // --- Incoming message handling ---
    /// @notice Handle incoming messages from Centrifuge. Parse the function params and forward to the corresponding
    ///         handler function.
    function handle(bytes calldata message) external;

    /// @notice Fulfills pending deposit requests after successful epoch execution on Centrifuge.
    ///         The amount of shares that can be claimed by the user is minted and moved to the escrow contract.
    ///         The MaxMint bookkeeping value is updated.
    ///         The request fulfillment can be partial.
    /// @dev    The shares in the escrow are reserved for the user and are transferred to the user on deposit
    ///         and mint calls.
    function fulfillDepositRequest(
        uint64 poolId,
        bytes16 trancheId,
        address user,
        uint128 assetId,
        uint128 assets,
        uint128 shares
    ) external;

    /// @notice Fulfills pending redeem requests after successful epoch execution on Centrifuge.
    ///         The amount of redeemed shares is burned. The amount of assets that can be claimed by the user in
    ///         return is locked in the escrow contract. The MaxWithdraw bookkeeping value is updated.
    ///         The request fulfillment can be partial.
    /// @dev    The assets in the escrow are reserved for the user and are transferred to the user on redeem
    ///         and withdraw calls.
    function fulfillRedeemRequest(
        uint64 poolId,
        bytes16 trancheId,
        address user,
        uint128 assetId,
        uint128 assets,
        uint128 shares
    ) external;

    /// @notice Fulfills deposit request cancellation after successful epoch execution on Centrifuge.
    ///         The amount of assets that can be claimed by the user is locked in the escrow contract.
    ///         Updates claimableCancelDepositRequest bookkeeping value. The cancellation order execution can be
    ///         partial.
    /// @dev    The assets in the escrow are reserved for the user and are transferred to the user during
    ///         claimCancelDepositRequest calls.
    ///         `fulfillment` represents the decrease in `pendingDepositRequest`.
    ///         This is a separate parameter from `assets` since there can be some precision loss when calculating this,
    ///         which would lead to having dust in the pendingDepositRequest value and
    ///         never closing out the request even after it is technically fulfilled.
    ///
    ///         Example:
    ///         User deposits 100 units of the vaults underlying asset.
    ///         - At some point they make cancellation request. The order in which is not guaranteed
    ///         Both requests arrive at CentrifugeChain. If the cancellation is first then all of the
    ///         deposited amount will be cancelled.
    ///
    ///         - There is the case where the deposit event is first and it gets completely fulfilled then
    ///         No amount of the deposited asset will be cancelled.
    ///
    ///         - There is the case where partially the deposit request is fulfilled. Let's say 40 units.
    ///         Then the cancel request arrives.
    ///         The remaining amount of deposited funds which is 60 units will cancelled.
    ///         There is a scenario where the deposit funds might different from the pool currency so some
    ///         swapping might happen. Either during this swapping or some fee collection or rounding there will be
    ///         difference between the actual amount that will be returned to the user.
    ///         `fulfillment` in this case will be 60 units but assets will be some lower amount because of the
    ///         aforementioned reasons
    ///         Let's assume the `asset` is 59. The user will be able to take back these 59 but
    ///         in order to not let any dust, we use `fulfillment` in our calculations.
    ///
    ///         `pendingDepositRequest` not necessary gets zeroed during this cancellation event.
    ///         When CentrifugeChain process the cancel event on its side, part of the deposit might be fulfilled.
    ///         In such case the chain will send two messages, one `fulfillDepositRequest` and one
    ///         `fulfillCancelDepositRequest`. In the example above, given the 100 units
    ///         deposited, 40 units are fulfilled and 60 can be cancelled.
    ///         The two messages sent from CentrifugeChain are not guaranteed to arrive in order.
    ///
    ///         Assuming first is the `fulfillCancelDepositRequest` the `pendingDepositRequest` here will be reduced to
    ///         60 units only. Then the `fulfillCancelDepositRequest` arrives with `fulfillment` 60. This amount is
    ///         removed from `pendingDepositRequests`. Since there are not more pendingDepositRequest` the
    ///         `pendingCancelDepositRequest` gets deleted.
    ///
    ///         Assuming first the `fulfillCancelDepositRequest` arrives then the `pendingDepositRequest` will be 100.
    ///         `fulfillment` is 60 so we are left with `pendingDepositRequest` equals to 40 ( 100 - 60 ).
    ///         Then the second message arrives which is `fulfillDepositRequest`. ( Check `fulfillDepositRequest`
    ///         implementation for details.)
    ///         When it arrives the `pendingDepositRequest` is 40 and the assets is 40
    ///         so there are no more `pendingDepositRequest` and right there the `pendingCancelDepositRequest will be
    ///         deleted.
    function fulfillCancelDepositRequest(
        uint64 poolId,
        bytes16 trancheId,
        address user,
        uint128 assetId,
        uint128 assets,
        uint128 fulfillment
    ) external;

    /// @notice Fulfills redeem request cancellation after successful epoch execution on Centrifuge.
    ///         The amount of shares that can be claimed by the user is locked in the escrow contract.
    ///         Updates claimableCancelRedeemRequest bookkeeping value. The cancellation order execution can also be
    ///         partial.
    /// @dev    The shares in the escrow are reserved for the user and are transferred to the user during
    ///         claimCancelRedeemRequest calls.
    function fulfillCancelRedeemRequest(uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 shares)
        external;

    /// @notice Triggers a redeem request on behalf of the user through Centrifuge governance.
    ///         This function is required for legal/compliance reasons and rare scenarios, like share contract
    ///         migrations.
    ///         Once the next epoch is executed on Centrifuge, vaults can proceed with asset payouts in case the orders
    ///         got fulfilled.
    /// @dev    The user share amount required to fulfill the redeem request has to be locked in escrow,
    ///         even though the asset payout can only happen after epoch execution.
    function triggerRedeemRequest(uint64 poolId, bytes16 trancheId, address user, uint128 assetId, uint128 shares)
        external;

    // --- View functions ---
    /// @notice Converts the assets value to share decimals.
    function convertToShares(address vault, uint256 _assets) external view returns (uint256 shares);

    /// @notice Converts the shares value to assets decimals.
    function convertToAssets(address vault, uint256 _shares) external view returns (uint256 assets);

    /// @notice Returns the max amount of assets based on the unclaimed amount of shares after at least one successful
    ///         deposit order fulfillment on Centrifuge.
    function maxDeposit(address vault, address user) external view returns (uint256);

    /// @notice Returns the max amount of shares a user can claim after at least one successful deposit order
    ///         fulfillment on Centrifuge.
    function maxMint(address vault, address user) external view returns (uint256 shares);

    /// @notice Returns the max amount of assets a user can claim after at least one successful redeem order fulfillment
    ///         on Centrifuge.
    function maxWithdraw(address vault, address user) external view returns (uint256 assets);

    /// @notice Returns the max amount of shares based on the unclaimed number of assets after at least one successful
    ///         redeem order fulfillment on Centrifuge.
    function maxRedeem(address vault, address user) external view returns (uint256 shares);

    /// @notice Indicates whether a user has pending deposit requests and returns the total deposit request asset
    /// request value.
    function pendingDepositRequest(address vault, address user) external view returns (uint256 assets);

    /// @notice Indicates whether a user has pending redeem requests and returns the total share request value.
    function pendingRedeemRequest(address vault, address user) external view returns (uint256 shares);

    /// @notice Indicates whether a user has pending deposit request cancellations.
    function pendingCancelDepositRequest(address vault, address user) external view returns (bool isPending);

    /// @notice Indicates whether a user has pending redeem request cancellations.
    function pendingCancelRedeemRequest(address vault, address user) external view returns (bool isPending);

    /// @notice Indicates whether a user has claimable deposit request cancellation and returns the total claim
    ///         value in assets.
    function claimableCancelDepositRequest(address vault, address user) external view returns (uint256 assets);

    /// @notice Indicates whether a user has claimable redeem request cancellation and returns the total claim
    ///         value in shares.
    function claimableCancelRedeemRequest(address vault, address user) external view returns (uint256 shares);

    /// @notice Returns the timestamp of the last share price update for a vault.
    function priceLastUpdated(address vault) external view returns (uint64 lastUpdated);

    // --- Vault claim functions ---
    /// @notice Processes owner's asset deposit after the epoch has been executed on Centrifuge and the deposit order
    ///         has been successfully processed (partial fulfillment possible).
    ///         Shares are transferred from the escrow to the receiver. Amount of shares is computed based of the amount
    ///         of assets and the owner's share price.
    /// @dev    The assets required to fulfill the deposit are already locked in escrow upon calling requestDeposit.
    ///         The shares required to fulfill the deposit have already been minted and transferred to the escrow on
    ///         fulfillDepositRequest.
    ///         Receiver has to pass all the share token restrictions in order to receive the shares.
    function deposit(address vault, uint256 assets, address receiver, address owner)
        external
        returns (uint256 shares);

    /// @notice Processes owner's share mint after the epoch has been executed on Centrifuge and the deposit order has
    ///         been successfully processed (partial fulfillment possible).
    ///         Shares are transferred from the escrow to the receiver. Amount of assets is computed based of the amount
    ///         of shares and the owner's share price.
    /// @dev    The assets required to fulfill the mint are already locked in escrow upon calling requestDeposit.
    ///         The shares required to fulfill the mint have already been minted and transferred to the escrow on
    ///         fulfillDepositRequest.
    ///         Receiver has to pass all the share token restrictions in order to receive the shares.
    function mint(address vault, uint256 shares, address receiver, address owner) external returns (uint256 assets);

    /// @notice Processes owner's share redemption after the epoch has been executed on Centrifuge and the redeem order
    ///         has been successfully processed (partial fulfillment possible).
    ///         Assets are transferred from the escrow to the receiver. Amount of assets is computed based of the amount
    ///         of shares and the owner's share price.
    /// @dev    The shares required to fulfill the redemption were already locked in escrow on requestRedeem and burned
    ///         on fulfillRedeemRequest.
    ///         The assets required to fulfill the redemption have already been reserved in escrow on
    ///         fulfillRedeemtRequest.
    function redeem(address vault, uint256 shares, address receiver, address owner) external returns (uint256 assets);

    /// @notice Processes owner's asset withdrawal after the epoch has been executed on Centrifuge and the redeem order
    ///         has been successfully processed (partial fulfillment possible).
    ///         Assets are transferred from the escrow to the receiver. Amount of shares is computed based of the amount
    ///         of shares and the owner's share price.
    /// @dev    The shares required to fulfill the withdrawal were already locked in escrow on requestRedeem and burned
    ///         on fulfillRedeemRequest.
    ///         The assets required to fulfill the withdrawal have already been reserved in escrow on
    ///         fulfillRedeemtRequest.
    function withdraw(address vault, uint256 assets, address receiver, address owner)
        external
        returns (uint256 shares);

    /// @notice Processes owner's deposit request cancellation after the epoch has been executed on Centrifuge and the
    ///         deposit order cancellation has been successfully processed (partial fulfillment possible).
    ///         Assets are transferred from the escrow to the receiver.
    /// @dev    The assets required to fulfill the claim have already been reserved for the owner in escrow on
    ///         fulfillCancelDepositRequest.
    function claimCancelDepositRequest(address vault, address receiver, address owner)
        external
        returns (uint256 assets);

    /// @notice Processes owner's redeem request cancellation after the epoch has been executed on Centrifuge and the
    ///         redeem order cancellation has been successfully processed (partial fulfillment possible).
    ///         Shares are transferred from the escrow to the receiver.
    /// @dev    The shares required to fulfill the claim have already been reserved for the owner in escrow on
    ///         fulfillCancelRedeemRequest.
    ///         Receiver has to pass all the share token restrictions in order to receive the shares.
    function claimCancelRedeemRequest(address vault, address receiver, address owner)
        external
        returns (uint256 shares);
}

Read Contract

AUTHORIZE_OPERATOR_TYPEHASH 0x0d62c332 → bytes32
DOMAIN_SEPARATOR 0x3644e515 → bytes32
asset 0x38d52e0f → address
authorizations 0xcdf5bba3 → bool
claimableCancelDepositRequest 0xaabb5922 → uint256
claimableCancelRedeemRequest 0xb04a5e05 → uint256
claimableDepositRequest 0x995ea21a → uint256
claimableRedeemRequest 0xeaed1d07 → uint256
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
deploymentChainId 0xcd0d0096 → uint256
escrow 0xe2fdcc17 → address
isOperator 0xb6363cf2 → bool
isPermissioned 0x78d77ecb → bool
manager 0x481c6a75 → address
maxDeposit 0x402d267d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxWithdraw 0xce96cb77 → uint256
pendingCancelDepositRequest 0xf15b65b1 → bool
pendingCancelRedeemRequest 0x7c1b50c4 → bool
pendingDepositRequest 0x26c6f96c → uint256
pendingRedeemRequest 0xf5a23d8d → uint256
poolId 0x3e0dc34e → uint64
previewDeposit 0xef8b30f7 → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewWithdraw 0x0a28a477 → uint256
priceLastUpdated 0x52aaead8 → uint64
pricePerShare 0x99530b06 → uint256
root 0xebf0c717 → address
share 0xa8d5fd65 → address
supportsInterface 0x01ffc9a7 → bool
totalAssets 0x01e1d114 → uint256
trancheId 0xb7adb4c5 → bytes16
wards 0xbf353dbb → uint256

Write Contract 25 functions

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

authorizeOperator 0x711b58ff
address controller
address operator
bool approved
bytes32 nonce
uint256 deadline
bytes signature
returns: bool
cancelDepositRequest 0xb9cf0634
uint256
address controller
cancelRedeemRequest 0x2b9d9c1f
uint256
address controller
claimCancelDepositRequest 0x69d77a44
uint256
address receiver
address controller
returns: uint256
claimCancelRedeemRequest 0x00a06d19
uint256
address receiver
address controller
returns: uint256
deny 0x9c52a7f1
address user
deposit 0x2e2d2984
uint256 assets
address receiver
address controller
returns: uint256
deposit 0x6e553f65
uint256 assets
address receiver
returns: uint256
file 0xd4e8be83
bytes32 what
address data
invalidateNonce 0x234f0e3b
bytes32 nonce
mint 0x94bf804d
uint256 shares
address receiver
returns: uint256
mint 0xda39b3e7
uint256 shares
address receiver
address controller
returns: uint256
onCancelDepositClaimable 0x4b5fd666
address controller
uint256 assets
onCancelRedeemClaimable 0x84ce2bf7
address controller
uint256 shares
onDepositClaimable 0xa21ec79f
address controller
uint256 assets
uint256 shares
onRedeemClaimable 0x0263704b
address controller
uint256 assets
uint256 shares
onRedeemRequest 0x2cdfbae5
address controller
address owner
uint256 shares
recoverTokens 0x5f3e849f
address token
address to
uint256 amount
redeem 0xba087652
uint256 shares
address receiver
address controller
returns: uint256
rely 0x65fae35e
address user
requestDeposit 0x85b77f45
uint256 assets
address controller
address owner
returns: uint256
requestRedeem 0x7d41c86e
uint256 shares
address controller
address owner
returns: uint256
setEndorsedOperator 0x21f075ee
address owner
bool approved
setOperator 0x558a7297
address operator
bool approved
returns: bool
withdraw 0xb460af94
uint256 assets
address receiver
address controller
returns: uint256

Recent Transactions

No transactions found for this address