Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x9e0457B5BcD95F4e2fc7FabCC41faAD0D443B4F7
Balance 0 ETH
Nonce 1
Code Size 18817 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

18817 bytes
0x608060405234801561001057600080fd5b50600436106102765760003560e01c80639c82f2a411610160578063cbda3f69116100d8578063e5331e911161008c578063ebbc496511610071578063ebbc496514610724578063eedc07131461072c578063f8fd31001461073f57600080fd5b8063e5331e91146106fe578063e9e11f101461071157600080fd5b8063daeccc79116100bd578063daeccc79146106c0578063e322ad2b146106ee578063e4e88954146106f657600080fd5b8063cbda3f6914610655578063d8fbc8331461069957600080fd5b8063b1022fdf1161012f578063bf1eb64a11610114578063bf1eb64a14610627578063bfccf0ec1461062f578063cb2af14b1461064257600080fd5b8063b1022fdf14610601578063b1f8100d1461061457600080fd5b80639c82f2a4146105b55780639e784426146105c8578063a4fb4120146105db578063a7229fd9146105ee57600080fd5b806342e4a4ac116101f357806352059756116101c25780636ed71ede116101a75780636ed71ede146104425780638da5cb5b1461046957806390c9427c1461047c57600080fd5b8063520597561461040957806362625c3f1461042f57600080fd5b806342e4a4ac146103ad578063456dc17a146103b65780634b3fd148146103dc5780634eb75f40146103ef57600080fd5b80632b3297f91161024a578063371fd8e61161022f578063371fd8e6146103425780633d33809d1461035557806340a647e21461037857600080fd5b80632b3297f91461031c578063354030231461032f57600080fd5b8062f714ce1461027b57806305b4591c146102a1578063070881d1146102b65780631e99f224146102f5575b600080fd5b61028e610289366004613d7b565b610747565b6040519081526020015b60405180910390f35b6102b46102af366004613da7565b6108e4565b005b6102dd7f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a781565b6040516001600160a01b039091168152602001610298565b6102dd7f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc81565b6005546102dd906001600160a01b031681565b6102b461033d366004613e23565b610c1b565b61028e610350366004613e23565b610dc0565b610368610363366004613e23565b610f6b565b6040519015158152602001610298565b610380610f93565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610298565b61028e60045481565b7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6102dd565b6102b46103ea366004613d7b565b6110e2565b600019805b60408051928352602083019190915201610298565b7f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816102dd565b6102b461043d366004613e3c565b611279565b61028e7f5e3e6b1e01c5708055548d82d01db741e37d03b948a7ef9f3d4b962648bcbfa781565b6000546102dd906001600160a01b031681565b6105a86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b6040516102989190613e71565b6102b46105c3366004613ebb565b611431565b6102b46105d6366004613e23565b61161e565b6102b46105e9366004613ebb565b61170b565b6102b46105fc366004613ed6565b6117b8565b6102b461060f366004613da7565b611929565b6102b4610622366004613ebb565b611be6565b61028e611cf5565b6102b461063d366004613f12565b611e5c565b6003546102dd906001600160a01b031681565b61067c7f0000000000000000000000000000000000000000000000000cb2bba6f17b800081565b6040516bffffffffffffffffffffffff9091168152602001610298565b6102dd7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb81565b6103686106ce366004613fb5565b600160209081526000928352604080842090915290825290205460ff1681565b61028e61202b565b61028e612031565b6103f461070c366004613e3c565b612087565b61028e61071f3660046140d8565b61224b565b6102b46124fb565b6103f461073a3660046140d8565b612596565b61028e61283f565b6003546000906001600160a01b031633146107a75761078a336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6107a757604051633006171960e21b815260040160405180910390fd5b6108dd83836108d86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b612975565b9392505050565b336001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb161461092d57604051633006171960e21b815260040160405180910390fd5b600061093b82840184614182565b90506000610a6b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b90506000610a7e83600001513084612975565b83519091508114610af55782516040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b8116600482015260248101919091526044015b60405180910390fd5b600554835160208501516040517fee534a3f0000000000000000000000000000000000000000000000000000000081526000936001600160a01b03169263ee534a3f92610b89927f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b8192917f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f91600401614207565b6020604051808303816000875af1158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc9190614239565b905086811015610c12576040517f2746152a0000000000000000000000000000000000000000000000000000000081526004810188905260248101829052604401610aec565b50505050505050565b6003546001600160a01b03163314610c7857610c5b336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b610c7857604051633006171960e21b815260040160405180910390fd5b610dbd81610da86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b60405180602001604052806000815250612a35565b50565b6003546000906001600160a01b03163314610e2057610e03336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b610e2057604051633006171960e21b815260040160405180910390fd5b610f6582610f506040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b60405180602001604052806000815250612ace565b92915050565b60006004546ec097ce7bc90715b34b9f1000000000610f8a91906142b0565b90911015919050565b600080600080600080610fa4612031565b95507f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611004573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110289190614239565b9450611032611cf5565b9350600061105187876ec097ce7bc90715b34b9f100000000084612d27565b9050611096816bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000cb2bba6f17b800016670de0b6b3a76400006000612d27565b9350846000036110aa5760001991506110d9565b6110bf85670de0b6b3a7640000836001612d27565b92506110d684670de0b6b3a7640000876000612d27565b91505b50909192939495565b6003546001600160a01b0316331461113f57611122336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61113f57604051633006171960e21b815260040160405180910390fd5b61127582826112706040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b612dc2565b5050565b6003546001600160a01b031633146112d6576112b9336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6112d657604051633006171960e21b815260040160405180910390fd5b60006114046040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b9050611420848260405180602001604052806000815250612a35565b61142b838383612dc2565b50505050565b61145f336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61147c57604051633006171960e21b815260040160405180910390fd5b6001600160a01b0381166114c7576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610aec565b6005546001600160a01b031680156115435761150e6001600160a01b037f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b8116826000612ee2565b6115436001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16826000612ee2565b6115796001600160a01b037f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b811683600019612ee2565b6115af6001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f1683600019612ee2565b6040516001600160a01b038316907f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990600090a250600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b61164c336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61166957604051633006171960e21b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff1681106116d0576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527f994e009790ba0b9928d4b193d3ba06a943b02485bae67e9c3af2e1955e065cf29060200160405180910390a150565b611739336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61175657604051633006171960e21b815260040160405180910390fd5b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f014e5e53fac070a4a01cf0bc7ae3d30d5ee46aed56c4029a5b6f8e0ad21f8a5890600090a250565b6117e6336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61180357604051633006171960e21b815260040160405180910390fd5b61180b611cf5565b156118c3577f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b0316836001600160a01b0316148061188157507f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b0316836001600160a01b0316145b156118c3576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610aec565b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f90968360405161190891815260200190565b60405180910390a36119246001600160a01b0384168383613018565b505050565b336001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb161461197257604051633006171960e21b815260040160405180910390fd5b600061198082840184614182565b90506000611ab06040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b9050611ac182600001513083612dc2565b600554825160208401516040517fee534a3f0000000000000000000000000000000000000000000000000000000081526000936001600160a01b03169263ee534a3f92611b55927f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f92917f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b8191600401614207565b6020604051808303816000875af1158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b989190614239565b905085811015611bde576040517f2746152a0000000000000000000000000000000000000000000000000000000081526004810187905260248101829052604401610aec565b505050505050565b611c14336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b611c3157604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611c7c576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610aec565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000611e57611e266040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b6001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb169030613061565b905090565b611e8a336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b611ea757604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611ef2576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610aec565b604080518082019091526000808252602082015260005b8281101561202457838382818110611f2357611f236142eb565b905060400201803603810190611f399190614328565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561201d81614367565b9050611f09565b5050505050565b6000611e575b6000611e576001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb167f5e3e6b1e01c5708055548d82d01db741e37d03b948a7ef9f3d4b962648bcbfa7306130b6565b60035460009081906001600160a01b031633146120e9576120cc336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6120e957604051633006171960e21b815260040160405180910390fd5b60006122176040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b9050612233868260405180602001604052806000815250612ace565b9250612240858583612975565b915050935093915050565b6003546000906001600160a01b031633146122ab5761228e336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6122ab57604051633006171960e21b815260040160405180910390fd5b60006123d96040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b9050612419868260405180604001604052808981526020018881525060405160200161240591906143a0565b604051602081830303815290604052612a35565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528692506000907f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b0316906370a0823190602401602060405180830381865afa15801561249c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c09190614239565b9050838111156124f1576124e4818360405180602001604052806000815250612a35565b6124ee81846143b3565b92505b5050949350505050565b6002546001600160a01b0316331461252657604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60035460009081906001600160a01b031633146125f8576125db336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6125f857604051633006171960e21b815260040160405180910390fd5b60006127266040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b031681526020017f000000000000000000000000e00bd3df25fb187d6abbb620b3dfd19839947b816001600160a01b031681526020017f00000000000000000000000038d130cee60cda080a3b3ac94c79c34b6fc919a76001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000cb2bba6f17b80006bffffffffffffffffffffffff16815250905090565b9050612766878260405180604001604052808a81526020018981525060405160200161275291906143a0565b604051602081830303815290604052612ace565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529093506000906001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16906370a0823190602401602060405180830381865afa1580156127e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280d9190614239565b90508481111561283457612831818360405180602001604052806000815250612ace565b92505b505094509492505050565b6000806128956001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb167f5e3e6b1e01c5708055548d82d01db741e37d03b948a7ef9f3d4b962648bcbfa7613169565b905060006128ec6001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb167f5e3e6b1e01c5708055548d82d01db741e37d03b948a7ef9f3d4b962648bcbfa761323d565b90508082116128fc576000612906565b61290681836143c6565b9250505090565b600080546001600160a01b03848116911614806108dd5750506001600160a01b03821660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915290205460ff1692915050565b60006000198414612986578361298e565b61298e612031565b6040517f8720316d0000000000000000000000000000000000000000000000000000000081529091506001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb1690638720316d906129fc9085908590309089906004016143d9565b600060405180830381600087803b158015612a1657600080fd5b505af1158015612a2a573d6000803e3d6000fd5b505050509392505050565b6040517f238d65790000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb169063238d657990612aa0908590879030908790600401614449565b600060405180830381600087803b158015612aba57600080fd5b505af1158015610c12573d6000803e3d6000fd5b600080612ad9611cf5565b90508015612d1f5780851015612ba1576040517f20b76e810000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb16906320b76e8190612b57908790899060009030908a906004016144c6565b60408051808303816000875af1158015612b75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b99919061453f565b509150612d1f565b6040517f93c520620000000000000000000000000000000000000000000000000000000081527f5e3e6b1e01c5708055548d82d01db741e37d03b948a7ef9f3d4b962648bcbfa760048201523060248201526000907f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb6001600160a01b0316906393c5206290604401606060405180830381865afa158015612c47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c6b9190614583565b602001516fffffffffffffffffffffffffffffffff1690507f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb6001600160a01b03166320b76e818660008430896040518663ffffffff1660e01b8152600401612cd89594939291906144c6565b60408051808303816000875af1158015612cf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d1a919061453f565b509250505b509392505050565b6000612d3485858561324c565b90506001826001811115612d4a57612d4a6145e8565b03612dba578280612d5d57612d5d614252565b84860915612dba57600019811015612d7757600101612dba565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610aec565b949350505050565b6040517f50d8cd4b0000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb16906350d8cd4b90612e329085908890869030908a90600401614617565b60408051808303816000875af1158015612e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e74919061453f565b50905083811461142b576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16600482015260248101829052604401610aec565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052612f618482613339565b61142b576040516001600160a01b03841660248201526000604482015261300e9085907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526133e0565b61142b84826133e0565b6040516001600160a01b0383166024820152604481018290526119249084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612faa565b60008061306f8460a0902090565b905060006130876001600160a01b03871683866134e2565b905060008061309688886135b8565b90945092506130aa915084905083836138cd565b98975050505050505050565b6000806130cb6130c685856138f2565b613967565b90506080856001600160a01b0316637784c685836040518263ffffffff1660e01b81526004016130fb919061468f565b600060405180830381865afa158015613118573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261314091908101906146d3565b600081518110613152576131526142eb565b6020026020010151901c60001c9150509392505050565b6000806131786130c6846139b2565b6040517f7784c6850000000000000000000000000000000000000000000000000000000081529091506001600160a01b03851690637784c685906131c090849060040161468f565b600060405180830381865afa1580156131dd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261320591908101906146d3565b600081518110613217576132176142eb565b602002602001015160001c6fffffffffffffffffffffffffffffffff1691505092915050565b6000806131786130c6846139f5565b60008080600019858709858702925082811083820303915050806000036132865783828161327c5761327c614252565b04925050506108dd565b8381106132d0576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610aec565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000806000846001600160a01b031684604051613356919061476d565b6000604051808303816000865af19150503d8060008114613393576040519150601f19603f3d011682016040523d82523d6000602084013e613398565b606091505b50915091508180156133c25750805115806133c25750808060200190518101906133c29190614789565b80156133d757506001600160a01b0385163b15155b95945050505050565b6000613435826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a169092919063ffffffff16565b90508051600014806134565750808060200190518101906134569190614789565b611924576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610aec565b6000806134f26130c685856138f2565b6040517f7784c6850000000000000000000000000000000000000000000000000000000081529091506001600160a01b03861690637784c6859061353a90849060040161468f565b600060405180830381865afa158015613557573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261357f91908101906146d3565b600081518110613591576135916142eb565b602002602001015160001c6fffffffffffffffffffffffffffffffff169150509392505050565b60008060008060006135cb8660a0902090565b6040517f5c60e39a000000000000000000000000000000000000000000000000000000008152600481018290529091506000906001600160a01b03891690635c60e39a9060240160c060405180830381865afa15801561362f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365391906147a6565b9050600081608001516fffffffffffffffffffffffffffffffff164261367991906143c6565b9050801580159061369f575060408201516fffffffffffffffffffffffffffffffff1615155b80156136b7575060608801516001600160a01b031615155b156138905760608801516040517f8c00bf6b0000000000000000000000000000000000000000000000000000000081526000916001600160a01b031690638c00bf6b9061370a908c908790600401614845565b602060405180830381865afa158015613727573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061374b9190614239565b9050600061377961375c8385613a25565b60408601516fffffffffffffffffffffffffffffffff1690613a90565b905061378481613aa5565b8460400181815161379591906148f1565b6fffffffffffffffffffffffffffffffff169052506137b381613aa5565b845185906137c29083906148f1565b6fffffffffffffffffffffffffffffffff90811690915260a08601511615905061388d5760006138118560a001516fffffffffffffffffffffffffffffffff1683613a9090919063ffffffff16565b905060006138598287600001516fffffffffffffffffffffffffffffffff1661383a91906143c6565b60208801518491906fffffffffffffffffffffffffffffffff16613b2e565b905061386481613aa5565b8660200181815161387591906148f1565b6fffffffffffffffffffffffffffffffff1690525050505b50505b508051602082015160408301516060909301516fffffffffffffffffffffffffffffffff9283169b9183169a509282169850911695509350505050565b6000612dba6138dd6001856143b3565b6138ea620f4240856143b3565b869190613b53565b6000600182846002604051602001613914929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b03909416908301528101919091526060016040516020818303038152906040528051906020012060001c6108dd91906143b3565b604080516001808252818301909252606091600091906020808301908036833701905050905082816000815181106139a1576139a16142eb565b602090810291909101015292915050565b6000808260036040516020016139d2929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c610f6591906143b3565b600060018260036040516020016139d2929190918252602082015260400190565b6060612dba8484600085613b7f565b600080613a328385614921565b90506000613a538280613a4e670de0b6b3a76400006002614921565b613c80565b90506000613a6f8284613a4e670de0b6b3a76400006003614921565b905080613a7c83856143b3565b613a8691906143b3565b9695505050505050565b60006108dd8383670de0b6b3a7640000613c80565b60408051808201909152601481527f6d61782075696e7431323820657863656564656400000000000000000000000060208201526000906fffffffffffffffffffffffffffffffff831115613b27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aec9190614938565b5090919050565b6000612dba613b40620f4240846143b3565b613b4b6001866143b3565b869190613c80565b600081613b616001826143c6565b613b6b8587614921565b613b7591906143b3565b612dba91906142b0565b606082471015613c11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610aec565b600080866001600160a01b03168587604051613c2d919061476d565b60006040518083038185875af1925050503d8060008114613c6a576040519150601f19603f3d011682016040523d82523d6000602084013e613c6f565b606091505b50915091506124ee87838387613c8d565b600081613b758486614921565b60608315613d16578251600003613d0f576001600160a01b0385163b613d0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aec565b5081612dba565b612dba8383815115613d2b5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aec9190614938565b80356001600160a01b0381168114613d7657600080fd5b919050565b60008060408385031215613d8e57600080fd5b82359150613d9e60208401613d5f565b90509250929050565b600080600060408486031215613dbc57600080fd5b83359250602084013567ffffffffffffffff80821115613ddb57600080fd5b818601915086601f830112613def57600080fd5b813581811115613dfe57600080fd5b876020828501011115613e1057600080fd5b6020830194508093505050509250925092565b600060208284031215613e3557600080fd5b5035919050565b600080600060608486031215613e5157600080fd5b8335925060208401359150613e6860408501613d5f565b90509250925092565b60a08101610f6582846001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b600060208284031215613ecd57600080fd5b6108dd82613d5f565b600080600060608486031215613eeb57600080fd5b613ef484613d5f565b9250613f0260208501613d5f565b9150604084013590509250925092565b600080600060408486031215613f2757600080fd5b613f3084613d5f565b9250602084013567ffffffffffffffff80821115613f4d57600080fd5b818601915086601f830112613f6157600080fd5b813581811115613f7057600080fd5b8760208260061b8501011115613e1057600080fd5b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114613d7657600080fd5b60008060408385031215613fc857600080fd5b613fd183613d5f565b9150613d9e60208401613f85565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561403157614031613fdf565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561406057614060613fdf565b604052919050565b600082601f83011261407957600080fd5b813567ffffffffffffffff81111561409357614093613fdf565b6140a66020601f19601f84011601614037565b8181528460208386010111156140bb57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156140ee57600080fd5b8435935060208501359250604085013567ffffffffffffffff81111561411357600080fd5b61411f87828801614068565b949793965093946060013593505050565b60006040828403121561414257600080fd5b61414a61400e565b905081358152602082013567ffffffffffffffff81111561416a57600080fd5b61417684828501614068565b60208301525092915050565b60006020828403121561419457600080fd5b813567ffffffffffffffff8111156141ab57600080fd5b612dba84828501614130565b60005b838110156141d25781810151838201526020016141ba565b50506000910152565b600081518084526141f38160208601602086016141b7565b601f01601f19169290920160200192915050565b60006001600160a01b03808716835285602084015280851660408401525060806060830152613a8660808301846141db565b60006020828403121561424b57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826142e6577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8015158114610dbd57600080fd5b60006040828403121561433a57600080fd5b61434261400e565b61434b83613f85565b8152602083013561435b8161431a565b60208201529392505050565b6000600019820361437a5761437a614281565b5060010190565b805182526000602082015160406020850152612dba60408501826141db565b6020815260006108dd6020830184614381565b80820180821115610f6557610f65614281565b81810381811115610f6557610f65614281565b610100810161442482876001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b600061010061449483886001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b8560a08401526001600160a01b03851660c08401528060e08401526144bb818401856141db565b979650505050505050565b600061012061451183896001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b8660a08401528560c08401526001600160a01b03851660e0840152806101008401526130aa818401856141db565b6000806040838503121561455257600080fd5b505080516020909101519092909150565b80516fffffffffffffffffffffffffffffffff81168114613d7657600080fd5b60006060828403121561459557600080fd5b6040516060810181811067ffffffffffffffff821117156145b8576145b8613fdf565b604052825181526145cb60208401614563565b60208201526145dc60408401614563565b60408201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b610120810161466282886001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b6020808252825182820181905260009190848201906040850190845b818110156146c7578351835292840192918401916001016146ab565b50909695505050505050565b600060208083850312156146e657600080fd5b825167ffffffffffffffff808211156146fe57600080fd5b818501915085601f83011261471257600080fd5b81518181111561472457614724613fdf565b8060051b9150614735848301614037565b818152918301840191848101908884111561474f57600080fd5b938501935b838510156130aa57845182529385019390850190614754565b6000825161477f8184602087016141b7565b9190910192915050565b60006020828403121561479b57600080fd5b81516108dd8161431a565b600060c082840312156147b857600080fd5b60405160c0810181811067ffffffffffffffff821117156147db576147db613fdf565b6040526147e783614563565b81526147f560208401614563565b602082015261480660408401614563565b604082015261481760608401614563565b606082015261482860808401614563565b608082015261483960a08401614563565b60a08201529392505050565b610160810161489082856001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b6fffffffffffffffffffffffffffffffff8084511660a08401528060208501511660c08401528060408501511660e084015280606085015116610100840152806080850151166101208401528060a085015116610140840152509392505050565b6fffffffffffffffffffffffffffffffff81811683821601908082111561491a5761491a614281565b5092915050565b8082028115828204841417610f6557610f65614281565b6020815260006108dd60208301846141db56fea26469706673582212201f05377ae2e3d07bee9d1d07e9a9c94d75dd9076d7486b17f960b8b095ac90e464736f6c63430008130033

Verified Source Code Full Match

Compiler: v0.8.19+commit.7dd6d404 EVM: paris Optimization: Yes (10000 runs)
Common.sol 672 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    // Handle non-overflow cases, 256 by 256 division.
    if (prod1 == 0) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

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

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

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

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

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

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

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

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

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

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

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

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

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}
OrigamiMath.sol 173 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OrigamiMath.sol)

import { mulDiv as prbMulDiv, PRBMath_MulDiv_Overflow } from "@prb/math/src/Common.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Utilities to operate on fixed point math multipliation and division
 * taking rounding into consideration
 */
library OrigamiMath {
    enum Rounding {
        ROUND_DOWN,
        ROUND_UP
    }

    uint256 public constant BASIS_POINTS_DIVISOR = 10_000;

    function scaleUp(uint256 amount, uint256 scalar) internal pure returns (uint256) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        return scalar == 1 ? amount : amount * scalar;
    }

    function scaleDown(
        uint256 amount, 
        uint256 scalar, 
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        unchecked {
            if (scalar == 1) {
                result = amount;
            } else if (roundingMode == Rounding.ROUND_DOWN) {
                result = amount / scalar;
            } else {
                // ROUND_UP uses the same logic as OZ Math.ceilDiv()
                result = amount == 0 ? 0 : (amount - 1) / scalar + 1;
            }
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision,
     * rounding up
     */
    function mulDiv(
        uint256 x, 
        uint256 y, 
        uint256 denominator,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        result = prbMulDiv(x, y, denominator);
        if (roundingMode == Rounding.ROUND_UP) {
            if (mulmod(x, y, denominator) != 0) {
                if (result < type(uint256).max) {
                    unchecked {
                        result = result + 1;
                    }
                } else {
                    revert PRBMath_MulDiv_Overflow(x, y, denominator);
                }
            }
        }
    }

    function subtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps;
        unchecked {
            numeratorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }

        result = basisPoints < BASIS_POINTS_DIVISOR
            ? mulDiv(
                inputAmount,
                numeratorBps, 
                BASIS_POINTS_DIVISOR, 
                roundingMode
            ) : 0;
    }

    function addBps(
        uint256 inputAmount,
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps;
        unchecked {
            numeratorBps = BASIS_POINTS_DIVISOR + basisPoints;
        }

        // Round up for max amounts out expected
        result = mulDiv(
            inputAmount,
            numeratorBps, 
            BASIS_POINTS_DIVISOR, 
            roundingMode
        );
    }

    /**
     * @notice Split the `inputAmount` into two parts based on the `basisPoints` fraction.
     * eg: 3333 BPS (33.3%) can be used to split an input amount of 600 into: (result=400, removed=200).
     * @dev The rounding mode is applied to the `result`
     */
    function splitSubtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result, uint256 removed) {
        result = subtractBps(inputAmount, basisPoints, roundingMode);
        unchecked {
            removed = inputAmount - result;
        }
    }

    /**
     * @notice Reverse the fractional amount of an input.
     * eg: For 3333 BPS (33.3%) and the remainder=400, the result is 600
     */
    function inverseSubtractBps(
        uint256 remainderAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        if (basisPoints == 0) return remainderAmount; // gas shortcut for 0
        if (basisPoints >= BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam();

        uint256 denominatorBps;
        unchecked {
            denominatorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }
        result = mulDiv(
            remainderAmount,
            BASIS_POINTS_DIVISOR, 
            denominatorBps, 
            roundingMode
        );
    }

    /**
     * @notice Calculate the relative difference of a value to a reference
     * @dev `value` and `referenceValue` must have the same precision
     * The denominator is always the referenceValue
     */
    function relativeDifferenceBps(
        uint256 value,
        uint256 referenceValue,
        Rounding roundingMode
    ) internal pure returns (uint256) {
        if (referenceValue == 0) revert CommonEventsAndErrors.InvalidParam();

        uint256 absDelta;
        unchecked {
            absDelta = value < referenceValue
                ? referenceValue - value
                : value - referenceValue;
        }

        return mulDiv(
            absDelta,
            BASIS_POINTS_DIVISOR,
            referenceValue,
            roundingMode
        );
    }
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
CommonEventsAndErrors.sol 20 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/CommonEventsAndErrors.sol)

/// @notice A collection of common events and errors thrown within the Origami contracts
library CommonEventsAndErrors {
    error InsufficientBalance(address token, uint256 required, uint256 balance);
    error InvalidToken(address token);
    error InvalidParam();
    error InvalidAddress(address addr);
    error InvalidAmount(address token, uint256 amount);
    error ExpectedNonZero();
    error Slippage(uint256 minAmountExpected, uint256 actualAmount);
    error IsPaused();
    error UnknownExecuteError(bytes returndata);
    error InvalidAccess();
    error BreachedMaxTotalSupply(uint256 totalSupply, uint256 maxTotalSupply);

    event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

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

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

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

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}
IIrm.sol 19 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {MarketParams, Market} from "./IMorpho.sol";

/// @title IIrm
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);

    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
    /// storage.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}
MathLib.sol 45 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

uint256 constant WAD = 1e18;

/// @title MathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
    /// @dev Returns (`x` * `y`) / `WAD` rounded down.
    function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded down.
    function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded up.
    function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y);
    }

    /// @dev Returns (`x` * `y`) / `d` rounded down.
    function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y) / d;
    }

    /// @dev Returns (`x` * `y`) / `d` rounded up.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y + (d - 1)) / d;
    }

    /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
    /// continuous compound interest rate.
    function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        uint256 firstTerm = x * n;
        uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
        uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);

        return firstTerm + secondTerm + thirdTerm;
    }
}
OrigamiElevatedAccess.sol 14 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { OrigamiElevatedAccessBase } from "contracts/common/access/OrigamiElevatedAccessBase.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccess is OrigamiElevatedAccessBase {
    constructor(address initialOwner) {
        _init(initialOwner);
    }
}
IMorpho.sol 353 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}

/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
    uint256 supplyShares;
    uint128 borrowShares;
    uint128 collateral;
}

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
}

struct Authorization {
    address authorizer;
    address authorized;
    bool isAuthorized;
    uint256 nonce;
    uint256 deadline;
}

struct Signature {
    uint8 v;
    bytes32 r;
    bytes32 s;
}

/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
    /// @notice The EIP-712 domain separator.
    /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
    /// the same chain id because the domain separator would be the same.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice The owner of the contract.
    /// @dev It has the power to change the owner.
    /// @dev It has the power to set fees on markets and set the fee recipient.
    /// @dev It has the power to enable but not disable IRMs and LLTVs.
    function owner() external view returns (address);

    /// @notice The fee recipient of all markets.
    /// @dev The recipient receives the fees of a given market through a supply position on that market.
    function feeRecipient() external view returns (address);

    /// @notice Whether the `irm` is enabled.
    function isIrmEnabled(address irm) external view returns (bool);

    /// @notice Whether the `lltv` is enabled.
    function isLltvEnabled(uint256 lltv) external view returns (bool);

    /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
    /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
    function isAuthorized(address authorizer, address authorized) external view returns (bool);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function nonce(address authorizer) external view returns (uint256);

    /// @notice Sets `newOwner` as `owner` of the contract.
    /// @dev Warning: No two-step transfer ownership.
    /// @dev Warning: The owner can be set to the zero address.
    function setOwner(address newOwner) external;

    /// @notice Enables `irm` as a possible IRM for market creation.
    /// @dev Warning: It is not possible to disable an IRM.
    function enableIrm(address irm) external;

    /// @notice Enables `lltv` as a possible LLTV for market creation.
    /// @dev Warning: It is not possible to disable a LLTV.
    function enableLltv(uint256 lltv) external;

    /// @notice Sets the `newFee` for the given market `marketParams`.
    /// @param newFee The new fee, scaled by WAD.
    /// @dev Warning: The recipient can be the zero address.
    function setFee(MarketParams memory marketParams, uint256 newFee) external;

    /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
    /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
    /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
    /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
    /// (funds could get stuck):
    /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
    /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
    /// `toSharesDown` overflow.
    /// - The IRM can revert on `borrowRate`.
    /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
    /// overflow.
    /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
    /// `liquidate` from being used under certain market conditions.
    /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
    /// the computation of `assetsRepaid` in `liquidate` overflow.
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

    /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupply` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
    /// amount of shares is given for full compatibility and precision.
    /// @dev Supplying a large amount can revert for overflow.
    /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to supply assets to.
    /// @param assets The amount of assets to supply.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased supply position.
    /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
    /// @return assetsSupplied The amount of assets supplied.
    /// @return sharesSupplied The amount of shares minted.
    function supply(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

    /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
    /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
    /// conversion roundings between shares and assets.
    /// @param marketParams The market to withdraw assets from.
    /// @param assets The amount of assets to withdraw.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the supply position.
    /// @param receiver The address that will receive the withdrawn assets.
    /// @return assetsWithdrawn The amount of assets withdrawn.
    /// @return sharesWithdrawn The amount of shares burned.
    function withdraw(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

    /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
    /// given for full compatibility and precision.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Borrowing a large amount can revert for overflow.
    /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to borrow assets from.
    /// @param assets The amount of assets to borrow.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased borrow position.
    /// @param receiver The address that will receive the borrowed assets.
    /// @return assetsBorrowed The amount of assets borrowed.
    /// @return sharesBorrowed The amount of shares minted.
    function borrow(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoReplay` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
    /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
    /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
    /// roundings between shares and assets.
    /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
    /// @param marketParams The market to repay assets to.
    /// @param assets The amount of assets to repay.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the debt position.
    /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
    /// @return assetsRepaid The amount of assets repaid.
    /// @return sharesRepaid The amount of shares burned.
    function repay(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupplyCollateral` function with the given `data`.
    /// @dev Interest are not accrued since it's not required and it saves gas.
    /// @dev Supplying a large amount can revert for overflow.
    /// @param marketParams The market to supply collateral to.
    /// @param assets The amount of collateral to supply.
    /// @param onBehalf The address that will own the increased collateral position.
    /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
    function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
        external;

    /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
    /// @param marketParams The market to withdraw collateral from.
    /// @param assets The amount of collateral to withdraw.
    /// @param onBehalf The address of the owner of the collateral position.
    /// @param receiver The address that will receive the collateral assets.
    function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
        external;

    /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
    /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
    /// `onMorphoLiquidate` function with the given `data`.
    /// @dev Either `seizedAssets` or `repaidShares` should be zero.
    /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
    /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
    /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
    /// @param marketParams The market of the position.
    /// @param borrower The owner of the position.
    /// @param seizedAssets The amount of collateral to seize.
    /// @param repaidShares The amount of shares to repay.
    /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
    /// @return The amount of assets seized.
    /// @return The amount of assets repaid.
    function liquidate(
        MarketParams memory marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 repaidShares,
        bytes memory data
    ) external returns (uint256, uint256);

    /// @notice Executes a flash loan.
    /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
    /// markets combined, plus donations).
    /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
    /// - `flashFee` is zero.
    /// - `maxFlashLoan` is the token's balance of this contract.
    /// - The receiver of `assets` is the caller.
    /// @param token The token to flash loan.
    /// @param assets The amount of assets to flash loan.
    /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
    function flashLoan(address token, uint256 assets, bytes calldata data) external;

    /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
    /// @param authorized The authorized address.
    /// @param newIsAuthorized The new authorization status.
    function setAuthorization(address authorized, bool newIsAuthorized) external;

    /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
    /// @dev Warning: Reverts if the signature has already been submitted.
    /// @dev The signature is malleable, but it has no impact on the security here.
    /// @dev The nonce is passed as argument to be able to revert with a different error message.
    /// @param authorization The `Authorization` struct.
    /// @param signature The signature.
    function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;

    /// @notice Accrues interest for the given market `marketParams`.
    function accrueInterest(MarketParams memory marketParams) external;

    /// @notice Returns the data stored on the different `slots`.
    function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}

/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user)
        external
        view
        returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
    /// accrual.
    function market(Id id)
        external
        view
        returns (
            uint128 totalSupplyAssets,
            uint128 totalSupplyShares,
            uint128 totalBorrowAssets,
            uint128 totalBorrowShares,
            uint128 lastUpdate,
            uint128 fee
        );

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id)
        external
        view
        returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user) external view returns (Position memory p);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
    /// interest accrual.
    function market(Id id) external view returns (Market memory m);

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id) external view returns (MarketParams memory);
}
IOracle.sol 15 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title IOracle
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface that oracles used by Morpho must implement.
/// @dev It is the user's responsibility to select markets with safe oracles.
interface IOracle {
    /// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36.
    /// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in
    /// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals`
    /// decimals of precision.
    function price() external view returns (uint256);
}
UtilsLib.sol 38 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {ErrorsLib} from "../libraries/ErrorsLib.sol";

/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
    /// @dev Returns true if there is exactly one zero among `x` and `y`.
    function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
        assembly {
            z := xor(iszero(x), iszero(y))
        }
    }

    /// @dev Returns the min of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns `x` safely cast to uint128.
    function toUint128(uint256 x) internal pure returns (uint128) {
        require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
        return uint128(x);
    }

    /// @dev Returns max(0, x - y).
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }
}
ErrorsLib.sol 80 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing error messages.
library ErrorsLib {
    /// @notice Thrown when the caller is not the owner.
    string internal constant NOT_OWNER = "not owner";

    /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
    string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded";

    /// @notice Thrown when the fee to set exceeds the maximum fee.
    string internal constant MAX_FEE_EXCEEDED = "max fee exceeded";

    /// @notice Thrown when the value is already set.
    string internal constant ALREADY_SET = "already set";

    /// @notice Thrown when the IRM is not enabled at market creation.
    string internal constant IRM_NOT_ENABLED = "IRM not enabled";

    /// @notice Thrown when the LLTV is not enabled at market creation.
    string internal constant LLTV_NOT_ENABLED = "LLTV not enabled";

    /// @notice Thrown when the market is already created.
    string internal constant MARKET_ALREADY_CREATED = "market already created";

    /// @notice Thrown when a token to transfer doesn't have code.
    string internal constant NO_CODE = "no code";

    /// @notice Thrown when the market is not created.
    string internal constant MARKET_NOT_CREATED = "market not created";

    /// @notice Thrown when not exactly one of the input amount is zero.
    string internal constant INCONSISTENT_INPUT = "inconsistent input";

    /// @notice Thrown when zero assets is passed as input.
    string internal constant ZERO_ASSETS = "zero assets";

    /// @notice Thrown when a zero address is passed as input.
    string internal constant ZERO_ADDRESS = "zero address";

    /// @notice Thrown when the caller is not authorized to conduct an action.
    string internal constant UNAUTHORIZED = "unauthorized";

    /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
    string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral";

    /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
    string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity";

    /// @notice Thrown when the position to liquidate is healthy.
    string internal constant HEALTHY_POSITION = "position is healthy";

    /// @notice Thrown when the authorization signature is invalid.
    string internal constant INVALID_SIGNATURE = "invalid signature";

    /// @notice Thrown when the authorization signature is expired.
    string internal constant SIGNATURE_EXPIRED = "signature expired";

    /// @notice Thrown when the nonce is invalid.
    string internal constant INVALID_NONCE = "invalid nonce";

    /// @notice Thrown when a token transfer reverted.
    string internal constant TRANSFER_REVERTED = "transfer reverted";

    /// @notice Thrown when a token transfer returned false.
    string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false";

    /// @notice Thrown when a token transferFrom reverted.
    string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";

    /// @notice Thrown when a token transferFrom returned false
    string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false";

    /// @notice Thrown when the maximum uint128 is exceeded.
    string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
}
OrigamiElevatedAccessBase.sol 85 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { IOrigamiElevatedAccess } from "contracts/interfaces/common/access/IOrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccessBase is IOrigamiElevatedAccess {
    /**
     * @notice The address of the current owner.
     */ 
    address public override owner;

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;

    /// @dev Track proposed owner
    address private _proposedNewOwner;

    function _init(address initialOwner) internal {
        if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess();
        if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        owner = initialOwner;
    }

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external override onlyElevatedAccess {
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account);
        emit NewOwnerProposed(owner, _proposedNewOwner, account);
        _proposedNewOwner = account;
    }

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external override {
        if (msg.sender != _proposedNewOwner) revert CommonEventsAndErrors.InvalidAccess();

        emit NewOwnerAccepted(owner, msg.sender);
        owner = msg.sender;
        delete _proposedNewOwner;
    }

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
        if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress(allowedCaller);
        ExplicitAccess memory _access;
        for (uint256 i; i < access.length; ++i) {
            _access = access[i];
            emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
            explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
        }
    }

    function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
        return (
            caller == owner || 
            explicitFunctionAccess[caller][fnSelector]
        );
    }

    /**
     * @notice The owner is allowed to call, or if explicit access has been given to the caller.
     * @dev Important: Only for use when called from an *external* contract. 
     * If a function with this modifier is called internally then the `msg.sig` 
     * will still refer to the top level externally called function.
     */
    modifier onlyElevatedAccess() {
        if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }
}
ConstantsLib.sol 21 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @dev The maximum fee a market can have (25%).
uint256 constant MAX_FEE = 0.25e18;

/// @dev Oracle price scale.
uint256 constant ORACLE_PRICE_SCALE = 1e36;

/// @dev Liquidation cursor.
uint256 constant LIQUIDATION_CURSOR = 0.3e18;

/// @dev Max liquidation incentive factor.
uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18;

/// @dev The EIP-712 typeHash for EIP712Domain.
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");

/// @dev The EIP-712 typeHash for Authorization.
bytes32 constant AUTHORIZATION_TYPEHASH =
    keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)");
SharesMathLib.sol 45 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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

/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
    using MathLib for uint256;

    /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
    /// high precision computations.
    /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
    /// stays low enough not to inflate these assets to a significant value.
    /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
    uint256 internal constant VIRTUAL_SHARES = 1e6;

    /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
    /// empty.
    uint256 internal constant VIRTUAL_ASSETS = 1;

    /// @dev Calculates the value of `assets` quoted in shares, rounding down.
    function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
    }

    /// @dev Calculates the value of `shares` quoted in assets, rounding down.
    function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
    }

    /// @dev Calculates the value of `assets` quoted in shares, rounding up.
    function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
    }

    /// @dev Calculates the value of `shares` quoted in assets, rounding up.
    function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
    }
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

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

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
IOrigamiSwapper.sol 29 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/swappers/IOrigamiSwapper.sol)

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

/**
 * @notice An on chain swapper contract to integrate with the 1Inch router | 0x proxy, 
 * possibly others which obtain quote calldata offchain and then execute via a low level call
 * to perform the swap onchain
 */
interface IOrigamiSwapper {
    error UnknownSwapError(bytes result);
    error InvalidSwap();
    error InvalidRouter(address router);

    event Swap(address indexed sellToken, uint256 sellTokenAmount, address indexed buyToken, uint256 buyTokenAmount);
    event RouterWhitelisted(address indexed router, bool allowed);

    /**
     * @notice Pull tokens from sender then execute the swap
     */
    function execute(
        IERC20 sellToken, 
        uint256 sellTokenAmount, 
        IERC20 buyToken,
        bytes memory swapData
    ) external returns (uint256 buyTokenAmount);
}
MarketParamsLib.sol 21 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id, MarketParams} from "../interfaces/IMorpho.sol";

/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to convert a market to its id.
library MarketParamsLib {
    /// @notice The length of the data used to compute the id of a market.
    /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
    uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;

    /// @notice Returns the id of the market `marketParams`.
    function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
        assembly ("memory-safe") {
            marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
        }
    }
}
IMorphoCallbacks.sol 52 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title IMorphoLiquidateCallback
/// @notice Interface that liquidators willing to use `liquidate`'s callback must implement.
interface IMorphoLiquidateCallback {
    /// @notice Callback called when a liquidation occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param repaidAssets The amount of repaid assets.
    /// @param data Arbitrary data passed to the `liquidate` function.
    function onMorphoLiquidate(uint256 repaidAssets, bytes calldata data) external;
}

/// @title IMorphoRepayCallback
/// @notice Interface that users willing to use `repay`'s callback must implement.
interface IMorphoRepayCallback {
    /// @notice Callback called when a repayment occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of repaid assets.
    /// @param data Arbitrary data passed to the `repay` function.
    function onMorphoRepay(uint256 assets, bytes calldata data) external;
}

/// @title IMorphoSupplyCallback
/// @notice Interface that users willing to use `supply`'s callback must implement.
interface IMorphoSupplyCallback {
    /// @notice Callback called when a supply occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of supplied assets.
    /// @param data Arbitrary data passed to the `supply` function.
    function onMorphoSupply(uint256 assets, bytes calldata data) external;
}

/// @title IMorphoSupplyCollateralCallback
/// @notice Interface that users willing to use `supplyCollateral`'s callback must implement.
interface IMorphoSupplyCollateralCallback {
    /// @notice Callback called when a supply of collateral occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of supplied collateral.
    /// @param data Arbitrary data passed to the `supplyCollateral` function.
    function onMorphoSupplyCollateral(uint256 assets, bytes calldata data) external;
}

/// @title IMorphoFlashLoanCallback
/// @notice Interface that users willing to use `flashLoan`'s callback must implement.
interface IMorphoFlashLoanCallback {
    /// @notice Callback called when a flash loan occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of assets that was flash loaned.
    /// @param data Arbitrary data passed to the `flashLoan` function.
    function onMorphoFlashLoan(uint256 assets, bytes calldata data) external;
}
MorphoLib.sol 63 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";

/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
    function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user));
        return uint256(morpho.extSloads(slot)[0]);
    }

    function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function fee(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function _array(bytes32 x) private pure returns (bytes32[] memory) {
        bytes32[] memory res = new bytes32[](1);
        res[0] = x;
        return res;
    }
}
OrigamiMorphoBorrowAndLend.sol 556 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/borrowAndLend/OrigamiMorphoBorrowAndLend.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { 
    IMorpho,
    Id as MorphoMarketId,
    MarketParams as MorphoMarketParams
} from "@morpho-org/morpho-blue/src/interfaces/IMorpho.sol";
import { IOracle as IMorphoOracle } from "@morpho-org/morpho-blue/src/interfaces/IOracle.sol";
import { ORACLE_PRICE_SCALE as MORPHO_ORACLE_PRICE_SCALE } from "@morpho-org/morpho-blue/src/libraries/ConstantsLib.sol";
import { IMorphoSupplyCollateralCallback } from "@morpho-org/morpho-blue/src/interfaces/IMorphoCallbacks.sol";
import { MorphoBalancesLib } from "@morpho-org/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol";
import { MorphoLib } from "@morpho-org/morpho-blue/src/libraries/periphery/MorphoLib.sol";
import { MarketParamsLib } from "@morpho-org/morpho-blue/src/libraries/MarketParamsLib.sol";

import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";
import { IOrigamiMorphoBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";

/**
 * @notice An Origami abstraction over a borrow/lend money market for
 * a single `supplyToken` and a single `borrowToken`.
 * This is a Morpho specific interface
 */
contract OrigamiMorphoBorrowAndLend is IOrigamiMorphoBorrowAndLend, IMorphoSupplyCollateralCallback, OrigamiElevatedAccess {
    using SafeERC20 for IERC20;
    using MorphoBalancesLib for IMorpho;
    using MorphoLib for IMorpho;
    using MarketParamsLib for MorphoMarketParams;
    using OrigamiMath for uint256;

    /**
     * @notice The morpho singleton contract
     */
    IMorpho public immutable override morpho;

    /**
     * @notice The token supplied as collateral
     */
    IERC20 private immutable _supplyToken;
    
    /**
     * @notice The token which is borrowed
     */
    IERC20 private immutable _borrowToken;

    /**
     * @notice The Morpho oracle used for the target market
     */
    address public override immutable morphoMarketOracle;

    /**
     * @notice The Morpho Interest Rate Model used for the target market
     */
    address public override immutable morphoMarketIrm;

    /**
     * @notice The Morpho Liquidation LTV for the target market
     */
    uint96 public override immutable morphoMarketLltv;

    /**
     * @notice The derived Morpho market ID given the market parameters
     */
    MorphoMarketId public override immutable marketId;

    /**
     * @notice The approved owner of the borrow/lend position
     */
    address public override positionOwner;

    /**
     * @notice The max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     */
    uint256 public override maxSafeLtv;
    
    /**
     * @notice The swapper for `borrowToken` <--> `supplyToken`
     */
    IOrigamiSwapper public override swapper;

    /**
     * @dev Factor when converting the Morpho LTV (1e18) to an Origami Assets/Liabilities (1e18)
     */
    uint256 private constant LTV_TO_AL_FACTOR = 1e36;

    /// @dev internal serialization of callback data for increasing leverage
    struct IncreaseLeverageData {
        /// @dev The amount of new `borrowToken` to borrow
        uint256 borrowAmount;

        /// @dev The encoded swap data for `borrowToken` to `supplyToken`
        bytes swapData;
    }

    /// @dev internal serialization of callback data for decreasing leverage
    struct DecreaseLeverageData {
        /// @dev The amount of `supplyToken` to withdraw from collateral
        uint256 withdrawCollateralAmount;

        /// @dev The encoded swap data for `supplyToken` to `borrowToken`
        bytes swapData;
    }
    
    constructor(
        address _initialOwner,
        address __supplyToken,
        address __borrowToken,
        address _morphoAddress,
        address _morphoMarketOracle, 
        address _morphoMarketIrm,
        uint96 _morphoMarketLltv,
        uint256 _maxSafeLtv
    ) OrigamiElevatedAccess(_initialOwner) {
        _supplyToken = IERC20(__supplyToken);
        _borrowToken = IERC20(__borrowToken);
        
        morpho = IMorpho(_morphoAddress);

        morphoMarketOracle = _morphoMarketOracle;
        morphoMarketIrm = _morphoMarketIrm;
        morphoMarketLltv = _morphoMarketLltv;

        if (_maxSafeLtv >= morphoMarketLltv) revert CommonEventsAndErrors.InvalidParam();
        maxSafeLtv = _maxSafeLtv;

        marketId = getMarketParams().id();

        // Verify that the market is valid
        if (morpho.lastUpdate(marketId) == 0) revert CommonEventsAndErrors.InvalidParam();

        // Approve the supply and borrow to the Morpho singleton upfront
        _supplyToken.forceApprove(_morphoAddress, type(uint256).max);
        _borrowToken.forceApprove(_morphoAddress, type(uint256).max);
    }

    /**
     * @notice Set the position owner who can borrow/lend via this contract
     */
    function setPositionOwner(address account) external override onlyElevatedAccess {
        positionOwner = account;
        emit PositionOwnerSet(account);
    }

    /**
     * @notice Set the max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     * so we set a more restrictive 'safe' LTV'
     */
    function setMaxSafeLtv(uint256 _maxSafeLtv) external override onlyElevatedAccess {
        if (_maxSafeLtv >= morphoMarketLltv) revert CommonEventsAndErrors.InvalidParam();
        maxSafeLtv = _maxSafeLtv;
        emit MaxSafeLtvSet(_maxSafeLtv);
    }
    
    /**
     * @notice Set the swapper responsible for `borrowToken` <--> `supplyToken` swaps
     */
    function setSwapper(address _swapper) external override onlyElevatedAccess {
        if (_swapper == address(0)) revert CommonEventsAndErrors.InvalidAddress(_swapper);

        // Update the approval's for both `supplyToken` and `borrowToken`
        address _oldSwapper = address(swapper);
        if (_oldSwapper != address(0)) {
            _supplyToken.forceApprove(_oldSwapper, 0);
            _borrowToken.forceApprove(_oldSwapper, 0);
        }
        _supplyToken.forceApprove(_swapper, type(uint256).max);
        _borrowToken.forceApprove(_swapper, type(uint256).max);

        emit SwapperSet(_swapper);
        swapper = IOrigamiSwapper(_swapper);
    }

    /**
     * @notice Supply tokens as collateral
     */
    function supply(
        uint256 supplyAmount
    ) external override onlyPositionOwnerOrElevated {
        _supply(supplyAmount, getMarketParams(), "");
    }

    /**
     * @notice Withdraw collateral tokens to recipient
     * @dev Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     */
    function withdraw(
        uint256 withdrawAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated returns (uint256 amountWithdrawn) {
        amountWithdrawn = _withdraw(withdrawAmount, recipient, getMarketParams());
    }

    /**
     * @notice Borrow tokens and send to recipient
     */
    function borrow(
        uint256 borrowAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated {
        _borrow(borrowAmount, recipient, getMarketParams());
    }

    /**
     * @notice Repay debt. 
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt balance.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repay(
        uint256 repayAmount
    ) external override onlyPositionOwnerOrElevated returns (uint256 debtRepaidAmount) {
        debtRepaidAmount = _repay(repayAmount, getMarketParams(), "");
    }

    /**
     * @notice Repay debt and withdraw collateral in one step
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt amount.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repayAndWithdraw(
        uint256 repayAmount, 
        uint256 withdrawAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated returns (uint256 debtRepaidAmount, uint256 withdrawnAmount) {
        MorphoMarketParams memory marketParams = getMarketParams();
        debtRepaidAmount = _repay(repayAmount, marketParams, "");
        withdrawnAmount = _withdraw(withdrawAmount, recipient, marketParams);
    }

    /**
     * @notice Supply collateral and borrow in one step
     */
    function supplyAndBorrow(
        uint256 supplyAmount, 
        uint256 borrowAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated {
        MorphoMarketParams memory marketParams = getMarketParams();
        _supply(supplyAmount, marketParams, "");
        _borrow(borrowAmount, recipient, marketParams);
    }

    /**
     * @notice Increase the leverage of the existing position, by supplying `supplyToken` as collateral
     * and borrowing `borrowToken` and swapping that back to `supplyToken`
     * @dev The totalCollateralSupplied may include any surplus after swapping from the debt to collateral
     */
    function increaseLeverage(
        uint256 supplyAmount,
        uint256 borrowAmount,
        bytes memory swapData,
        uint256 supplyCollateralSurplusThreshold
    ) external override onlyPositionOwnerOrElevated returns (uint256 totalCollateralSupplied) {
        MorphoMarketParams memory marketParams = getMarketParams();
        _supply(
            supplyAmount,
            marketParams,
            abi.encode(IncreaseLeverageData(
                borrowAmount,
                swapData
            ))
        );
        totalCollateralSupplied = supplyAmount;

        // There may be a suplus of `supplyToken` in this contract after the leverage increase
        // If over the threshold, supply any surplus back in as collateral to morpho
        uint256 surplusAfterLeverage = _supplyToken.balanceOf(address(this));
        if (surplusAfterLeverage > supplyCollateralSurplusThreshold) {
            _supply(surplusAfterLeverage, marketParams, "");
            totalCollateralSupplied = totalCollateralSupplied + surplusAfterLeverage;
        }
    }

    /**
     * @notice Callback called when a supply of collateral occurs in Morpho.
     * @dev The callback is called only if data is not empty.
     * @param supplyAmount The amount of supplied collateral.
     * @param data Arbitrary data passed to the `supplyCollateral` function.
     */
    function onMorphoSupplyCollateral(uint256 supplyAmount, bytes calldata data) external override {
        if (msg.sender != address(morpho)) revert CommonEventsAndErrors.InvalidAccess();
        IncreaseLeverageData memory decoded = abi.decode(data, (IncreaseLeverageData));

        MorphoMarketParams memory marketParams = getMarketParams();

        // Perform the borrow
        _borrow(decoded.borrowAmount, address(this), marketParams);

        // Swap from [borrowToken] to [supplyToken]
        // The expected amount of [supplyToken] received after swapping from [borrowToken]
        // needs to at least cover the supplyAmount
        uint256 collateralReceived = swapper.execute(_borrowToken, decoded.borrowAmount, _supplyToken, decoded.swapData);
        if (collateralReceived < supplyAmount) {
            revert CommonEventsAndErrors.Slippage(supplyAmount, collateralReceived);
        }
    }

    /**
     * @notice Decrease the leverage of the existing position, by repaying `borrowToken`
     * and withdrawing `supplyToken` collateral then swapping that back to `borrowToken`
     */
    function decreaseLeverage(
        uint256 repayAmount,
        uint256 withdrawCollateralAmount,
        bytes memory swapData,
        uint256 repaySurplusThreshold
    ) external override onlyPositionOwnerOrElevated returns (
        uint256 debtRepaidAmount, 
        uint256 surplusDebtRepaid
    ) {
        MorphoMarketParams memory marketParams = getMarketParams();
        debtRepaidAmount = _repay(
            repayAmount, 
            marketParams,
            abi.encode(DecreaseLeverageData(
                withdrawCollateralAmount,
                swapData
            ))
        );

        // There may be a suplus of `borrowToken` in this contract after the delverage
        // If over the threshold, repay any surplus back to morpho
        uint256 surplusAfterDeleverage = _borrowToken.balanceOf(address(this));
        if (surplusAfterDeleverage > repaySurplusThreshold) {
            surplusDebtRepaid = _repay(surplusAfterDeleverage, marketParams, "");
        }
    }

    /**
     * @notice Callback called when a repayment occurs.
     * @dev The callback is called only if data is not empty.
     * @param repayAmount The amount of repaid assets.
     * @param data Arbitrary data passed to the `repay` function.
     */
    function onMorphoRepay(uint256 repayAmount, bytes calldata data) external {
        if (msg.sender != address(morpho)) revert CommonEventsAndErrors.InvalidAccess();
        DecreaseLeverageData memory decoded = abi.decode(data, (DecreaseLeverageData));

        MorphoMarketParams memory marketParams = getMarketParams();

        // Withdraw collateral
        uint256 _amountWithdrawn = _withdraw(decoded.withdrawCollateralAmount, address(this), marketParams);
        if (_amountWithdrawn != decoded.withdrawCollateralAmount) {
            revert CommonEventsAndErrors.InvalidAmount(address(_supplyToken), decoded.withdrawCollateralAmount);
        }
        
        // Swap from [supplyToken] to [borrowToken]
        // The expected amount of [borrowToken] received after swapping from [supplyToken]
        // needs to at least cover the repayAmount
        uint256 borrowTokenReceived = swapper.execute(_supplyToken, decoded.withdrawCollateralAmount, _borrowToken, decoded.swapData);
        if (borrowTokenReceived < repayAmount) {
            revert CommonEventsAndErrors.Slippage(repayAmount, borrowTokenReceived);
        }
    }

    /**
     * @notice Recover accidental donations.
     * @dev Does not allow for recovery of supplyToken or borrowToken if there is an outstanding
     * morpho debt on this pool
     * @param token Token to recover
     * @param to Recipient address
     * @param amount Amount to recover
     */
    function recoverToken(address token, address to, uint256 amount) external onlyElevatedAccess {
        if (debtBalance() != 0) {
            if (token == address(_supplyToken) || token == address(_borrowToken)) {
                revert CommonEventsAndErrors.InvalidToken(token);
            }
        }
        emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
        IERC20(token).safeTransfer(to, amount);
    }
        
    /**
     * @notice The Morpho market parameters
     */
    function getMarketParams() public override view returns (MorphoMarketParams memory) {
        return MorphoMarketParams({
            loanToken: address(_borrowToken),
            collateralToken: address(_supplyToken),
            oracle: morphoMarketOracle,
            irm: morphoMarketIrm,
            lltv: morphoMarketLltv
        });
    }

    /**
     * @notice The token supplied as collateral
     */
    function supplyToken() public override view returns (address) {
        return address(_supplyToken);
    }
    
    /**
     * @notice The token which is borrowed
     */
    function borrowToken() public override view returns (address) {
        return address(_borrowToken);
    }

    /**
     * @notice The current (manually tracked) balance of tokens supplied
     */
    function suppliedBalance() public override view returns (uint256) {
        return morpho.collateral(marketId, address(this));
    }

    /**
     * @notice The current debt balance of tokens borrowed
     */
    function debtBalance() public override view returns (uint256) {
        return morpho.expectedBorrowAssets(getMarketParams(), address(this));
    }

    /**
     * @notice Whether a given Assets/Liabilities Ratio is safe, given the upstream
     * money market parameters
     */
    function isSafeAlRatio(uint256 alRatio) external override view returns (bool) {
        return alRatio >= LTV_TO_AL_FACTOR / maxSafeLtv;
    }

    /**
     * @notice How many `supplyToken` are available to withdraw from collateral
     * from the entire protocol, assuming this contract has fully paid down its debt
     */
    function availableToWithdraw() external override view returns (uint256) {
        // The collateral (for borrows) never gets used as they are siloed markets,
        // this contracts collateral is always available to be withdrawn.
        // There's no morpho metric for the entire collateral supplied, instead
        // this just returns our collateral - so the same as `suppliedBalance()`
        return suppliedBalance();
    }

    /**
     * @notice How many `borrowToken` are available to borrow
     * from the entire protocol
     */
    function availableToBorrow() external override view returns (uint256) {
        uint256 totalSupplyAssets = morpho.totalSupplyAssets(marketId);
        uint256 totalBorrowAssets = morpho.totalBorrowAssets(marketId);
        return totalSupplyAssets > totalBorrowAssets ? totalSupplyAssets - totalBorrowAssets : 0;
    }

    /**
     * @notice How much more capacity is available to supply
     */
    function availableToSupply() external override pure returns (
        uint256 supplyCap,
        uint256 available
    ) {
        return (
            type(uint256).max,
            type(uint256).max
        );
    }

    /**
     * @notice Returns the curent Morpho position data
     */
    function debtAccountData() external override view returns (
        uint256 collateral,
        uint256 collateralPrice,
        uint256 borrowed,
        uint256 maxBorrow,
        uint256 currentLtv,
        uint256 healthFactor
    ) {
        // supplyToken decimals
        collateral = suppliedBalance();
        // `36 + borrowToken decimals - supplyToken decimals` decimals of precision.
        collateralPrice = IMorphoOracle(morphoMarketOracle).price();
        // borrowToken decimals
        borrowed = debtBalance();
        
        uint256 _collateralInBorrowTerms = collateral.mulDiv(
            collateralPrice, 
            MORPHO_ORACLE_PRICE_SCALE, 
            OrigamiMath.Rounding.ROUND_DOWN
        );

        maxBorrow = _collateralInBorrowTerms.mulDiv(
            morphoMarketLltv,
            1e18,
            OrigamiMath.Rounding.ROUND_DOWN
        );
        
        if (borrowed == 0) {
            healthFactor = type(uint256).max;
        } else {
            currentLtv = borrowed.mulDiv(
                1e18,
                _collateralInBorrowTerms,
                OrigamiMath.Rounding.ROUND_UP
            );
            healthFactor = maxBorrow.mulDiv(1e18, borrowed, OrigamiMath.Rounding.ROUND_DOWN);
        }
    }

    function _supply(uint256 supplyAmount, MorphoMarketParams memory marketParams, bytes memory data) internal {
        morpho.supplyCollateral(marketParams, supplyAmount, address(this), data);
    }

    function _withdraw(uint256 withdrawAmount, address recipient, MorphoMarketParams memory marketParams) internal returns (uint256 amountWithdrawn) {
        // If `withdrawAmount` == uint256.max, then set the the current supplied collateral balance
        amountWithdrawn = withdrawAmount == type(uint256).max ? suppliedBalance() : withdrawAmount;
        morpho.withdrawCollateral(marketParams, amountWithdrawn, address(this), recipient);
    }

    function _borrow(uint256 borrowAmount, address recipient, MorphoMarketParams memory marketParams) internal {
        (uint256 assetsBorrowed,) = morpho.borrow(
            marketParams, borrowAmount, 0, address(this), recipient
        );
        if (assetsBorrowed != borrowAmount) revert CommonEventsAndErrors.InvalidAmount(address(_borrowToken), assetsBorrowed);
    }
    
    function _repay(uint256 repayAmount, MorphoMarketParams memory marketParams, bytes memory data) internal returns (uint256 debtRepaidAmount) {
        uint256 _debtBalance = debtBalance();

        if (_debtBalance != 0) {
            // If the repayment amount gte the current balance, then repay 100% of the debt.
            if (repayAmount < _debtBalance) {
                // Repay via the amount (not shares)
                (debtRepaidAmount, ) = morpho.repay(marketParams, repayAmount, 0, address(this), data);
            } else {
                // Calculate the current morpho shares owed, and repay via the shares (not amount)
                // Do this when equal to the debt balance to avoid Morpho rounding underflow
                uint256 _repayShares = morpho.position(marketId, address(this)).borrowShares;
                (debtRepaidAmount, ) = morpho.repay(marketParams, 0, _repayShares, address(this), data);
            }
        }
    }

    /**
     * @dev Only the positionOwner or Elevated Access is allowed to call.
     */
    modifier onlyPositionOwnerOrElevated() {
        if (msg.sender != address(positionOwner)) {
            if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        }
        _;
    }
}
IOrigamiElevatedAccess.sol 47 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/access/IOrigamiElevatedAccess.sol)

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
interface IOrigamiElevatedAccess {
    event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);

    event NewOwnerProposed(address indexed oldOwner, address indexed oldProposedOwner, address indexed newProposedOwner);
    event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);

    struct ExplicitAccess {
        bytes4 fnSelector;
        bool allowed;
    }

    /**
     * @notice The address of the current owner.
     */ 
    function owner() external view returns (address);

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external view returns (bool);

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external;

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external;

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @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.
 */
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].
     */
    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);
}
IOrigamiBorrowAndLend.sol 129 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/borrowAndLend/IOrigamiBorrowAndLend.sol)

/**
 * @notice An Origami abstraction over a borrow/lend money market for
 * a single `supplyToken` and a single `borrowToken`, for a given `positionOwner`
 */
interface IOrigamiBorrowAndLend {
    event PositionOwnerSet(address indexed account);
    event SurplusDebtReclaimed(uint256 amount, address indexed recipient);

    /**
     * @notice Set the position owner who can borrow/lend via this contract
     */
    function setPositionOwner(address account) external;

    /**
     * @notice Supply tokens as collateral
     */
    function supply(
        uint256 supplyAmount
    ) external;

    /**
     * @notice Withdraw collateral tokens to recipient
     * @dev Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     */
    function withdraw(
        uint256 withdrawAmount, 
        address recipient
    ) external returns (uint256 amountWithdrawn);

    /**
     * @notice Borrow tokens and send to recipient
     */
    function borrow(
        uint256 borrowAmount,
        address recipient
    ) external;

    /**
     * @notice Repay debt. 
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt balance.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repay(
        uint256 repayAmount
    ) external returns (uint256 debtRepaidAmount);

    /**
     * @notice Repay debt and withdraw collateral in one step
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt amount.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repayAndWithdraw(
        uint256 repayAmount, 
        uint256 withdrawAmount, 
        address recipient
    ) external returns (
        uint256 debtRepaidAmount,
        uint256 withdrawnAmount
    );

    /**
     * @notice Supply collateral and borrow in one step
     */
    function supplyAndBorrow(
        uint256 supplyAmount, 
        uint256 borrowAmount, 
        address recipient
    ) external;

    /**
     * @notice The approved owner of the borrow/lend position
     */
    function positionOwner() external view returns (address);

    /**
     * @notice The token supplied as collateral
     */
    function supplyToken() external view returns (address);

    /**
     * @notice The token which is borrowed
     */
    function borrowToken() external view returns (address);

    /**
     * @notice The current (manually tracked) balance of tokens supplied
     */
    function suppliedBalance() external view returns (uint256);

    /**
     * @notice The current debt balance of tokens borrowed
     */
    function debtBalance() external view returns (uint256);

    /**
     * @notice Whether a given Assets/Liabilities Ratio is safe, given the upstream
     * money market parameters
     */
    function isSafeAlRatio(uint256 alRatio) external view returns (bool);

    /**
     * @notice How many `supplyToken` are available to withdraw from collateral
     * from the entire protocol, assuming this contract has fully paid down its debt
     */
    function availableToWithdraw() external view returns (uint256);

    /**
     * @notice How much more capacity is available to supply
     */
    function availableToSupply() external view returns (
        uint256 supplyCap,
        uint256 available
    );

    /**
     * @notice How many `borrowToken` are available to borrow
     * from the entire protocol
     */
    function availableToBorrow() external view returns (uint256);
}
MorphoStorageLib.sol 109 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id} from "../../interfaces/IMorpho.sol";

/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
    /* SLOTS */

    uint256 internal constant OWNER_SLOT = 0;
    uint256 internal constant FEE_RECIPIENT_SLOT = 1;
    uint256 internal constant POSITION_SLOT = 2;
    uint256 internal constant MARKET_SLOT = 3;
    uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
    uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
    uint256 internal constant IS_AUTHORIZED_SLOT = 6;
    uint256 internal constant NONCE_SLOT = 7;
    uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8;

    /* SLOT OFFSETS */

    uint256 internal constant LOAN_TOKEN_OFFSET = 0;
    uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
    uint256 internal constant ORACLE_OFFSET = 2;
    uint256 internal constant IRM_OFFSET = 3;
    uint256 internal constant LLTV_OFFSET = 4;

    uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
    uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;

    uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
    uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
    uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;

    /* GETTERS */

    function ownerSlot() internal pure returns (bytes32) {
        return bytes32(OWNER_SLOT);
    }

    function feeRecipientSlot() internal pure returns (bytes32) {
        return bytes32(FEE_RECIPIENT_SLOT);
    }

    function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) {
        return bytes32(
            uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET
        );
    }

    function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) {
        return bytes32(
            uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT)))))
                + BORROW_SHARES_AND_COLLATERAL_OFFSET
        );
    }

    function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET);
    }

    function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET);
    }

    function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET);
    }

    function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
        return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
    }

    function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
        return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
    }

    function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) {
        return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT))));
    }

    function nonceSlot(address authorizer) internal pure returns (bytes32) {
        return keccak256(abi.encode(authorizer, NONCE_SLOT));
    }

    function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET);
    }

    function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET);
    }

    function idToOracleSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET);
    }

    function idToIrmSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET);
    }

    function idToLltvSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET);
    }
}
MorphoBalancesLib.sol 120 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";

import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";

/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
    using MathLib for uint256;
    using MathLib for uint128;
    using UtilsLib for uint256;
    using MorphoLib for IMorpho;
    using SharesMathLib for uint256;
    using MarketParamsLib for MarketParams;

    /// @notice Returns the expected market balances of a market after having accrued interest.
    /// @return The expected total supply assets.
    /// @return The expected total supply shares.
    /// @return The expected total borrow assets.
    /// @return The expected total borrow shares.
    function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256, uint256, uint256, uint256)
    {
        Id id = marketParams.id();
        Market memory market = morpho.market(id);

        uint256 elapsed = block.timestamp - market.lastUpdate;

        // Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
        if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
            uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
            uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
            market.totalBorrowAssets += interest.toUint128();
            market.totalSupplyAssets += interest.toUint128();

            if (market.fee != 0) {
                uint256 feeAmount = interest.wMulDown(market.fee);
                // The fee amount is subtracted from the total supply in this calculation to compensate for the fact
                // that total supply is already updated.
                uint256 feeShares =
                    feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares);
                market.totalSupplyShares += feeShares.toUint128();
            }
        }

        return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares);
    }

    /// @notice Returns the expected total supply assets of a market after having accrued interest.
    function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalSupplyAssets)
    {
        (totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected total borrow assets of a market after having accrued interest.
    function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalBorrowAssets)
    {
        (,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected total supply shares of a market after having accrued interest.
    function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalSupplyShares)
    {
        (, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
    /// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
    /// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from
    /// assets to shares.
    function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user)
        internal
        view
        returns (uint256)
    {
        Id id = marketParams.id();
        uint256 supplyShares = morpho.supplyShares(id, user);
        (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);

        return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
    }

    /// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest.
    /// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow
    /// assets.
    function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user)
        internal
        view
        returns (uint256)
    {
        Id id = marketParams.id();
        uint256 borrowShares = morpho.borrowShares(id, user);
        (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams);

        return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares);
    }
}
IOrigamiMorphoBorrowAndLend.sol 107 lines
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol)

import { IOrigamiBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiBorrowAndLend.sol";
import { IMorpho, Id as MorphoMarketId, MarketParams as MorphoMarketParams } from "@morpho-org/morpho-blue/src/interfaces/IMorpho.sol";
import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";

/**
 * @notice An Origami abstraction over a borrow/lend money market for
 * a single `supplyToken` and a single `borrowToken`.
 * This is a Morpho specific interface
 */
interface IOrigamiMorphoBorrowAndLend is IOrigamiBorrowAndLend {
    event MaxSafeLtvSet(uint256 _maxSafeLtv);
    event SwapperSet(address indexed swapper);

    /**
     * @notice Set the max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     */
    function setMaxSafeLtv(uint256 _maxSafeLtv) external;

    /**
     * @notice Set the swapper responsible for `borrowToken` <--> `supplyToken` swaps
     */
    function setSwapper(address _swapper) external;

    /**
     * @notice Increase the leverage of the existing position, by supplying `supplyToken` as collateral
     * and borrowing `borrowToken` and swapping that back to `supplyToken`
     * @dev The totalCollateralSupplied may include any surplus after swapping from the debt to collateral
     */
    function increaseLeverage(
        uint256 supplyCollateralAmount,
        uint256 borrowAmount,
        bytes memory swapData,
        uint256 supplyCollateralSurplusThreshold
    ) external returns (uint256 totalCollateralSupplied);

    /**
     * @notice Decrease the leverage of the existing position, by repaying `borrowToken`
     * and withdrawing `supplyToken` collateral then swapping that back to `borrowToken`
     */
    function decreaseLeverage(
        uint256 repayAmount,
        uint256 withdrawCollateralAmount,
        bytes memory swapData,
        uint256 repaySurplusThreshold
    ) external returns  (
        uint256 debtRepaidAmount, 
        uint256 surplusDebtRepaid
    );

    /**
     * @notice The morpho singleton contract
     */
    function morpho() external view returns (IMorpho);

    /**
     * @notice The Morpho oracle used for the target market
     */
    function morphoMarketOracle() external view returns (address);

    /**
     * @notice The Morpho Interest Rate Model used for the target market
     */
    function morphoMarketIrm() external view returns (address);

    /**
     * @notice The Morpho Liquidation LTV for the target market
     */
    function morphoMarketLltv() external view returns (uint96);

    /**
     * @notice The Morpho market parameters
     */
    function getMarketParams() external view returns (MorphoMarketParams memory);

    /**
     * @notice The derived Morpho market ID given the market parameters
     */
    function marketId() external view returns (MorphoMarketId);

    /**
     * @notice The max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     */
    function maxSafeLtv() external view returns (uint256);
    
    /**
     * @notice The swapper for `borrowToken` <--> `supplyToken`
     */
    function swapper() external view returns (IOrigamiSwapper);

    /**
     * @notice Returns the curent Morpho position data
     */
    function debtAccountData() external view returns (
        uint256 collateral,
        uint256 collateralPrice,
        uint256 borrowed,
        uint256 maxBorrow,
        uint256 currentLtv,
        uint256 healthFactor
    );
}

Read Contract

availableToBorrow 0xf8fd3100 → uint256
availableToSupply 0x4eb75f40 → uint256, uint256
availableToWithdraw 0xe322ad2b → uint256
borrowToken 0x456dc17a → address
debtAccountData 0x40a647e2 → uint256, uint256, uint256, uint256, uint256, uint256
debtBalance 0xbf1eb64a → uint256
explicitFunctionAccess 0xdaeccc79 → bool
getMarketParams 0x90c9427c → tuple
isSafeAlRatio 0x3d33809d → bool
marketId 0x6ed71ede → bytes32
maxSafeLtv 0x42e4a4ac → uint256
morpho 0xd8fbc833 → address
morphoMarketIrm 0x1e99f224 → address
morphoMarketLltv 0xcbda3f69 → uint96
morphoMarketOracle 0x070881d1 → address
owner 0x8da5cb5b → address
positionOwner 0xcb2af14b → address
suppliedBalance 0xe4e88954 → uint256
supplyToken 0x52059756 → address
swapper 0x2b3297f9 → address

Write Contract 17 functions

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

acceptOwner 0xebbc4965
No parameters
borrow 0x4b3fd148
uint256 borrowAmount
address recipient
decreaseLeverage 0xeedc0713
uint256 repayAmount
uint256 withdrawCollateralAmount
bytes swapData
uint256 repaySurplusThreshold
returns: uint256, uint256
increaseLeverage 0xe9e11f10
uint256 supplyAmount
uint256 borrowAmount
bytes swapData
uint256 supplyCollateralSurplusThreshold
returns: uint256
onMorphoRepay 0x05b4591c
uint256 repayAmount
bytes data
onMorphoSupplyCollateral 0xb1022fdf
uint256 supplyAmount
bytes data
proposeNewOwner 0xb1f8100d
address account
recoverToken 0xa7229fd9
address token
address to
uint256 amount
repay 0x371fd8e6
uint256 repayAmount
returns: uint256
repayAndWithdraw 0xe5331e91
uint256 repayAmount
uint256 withdrawAmount
address recipient
returns: uint256, uint256
setExplicitAccess 0xf48a954d
address allowedCaller
tuple[] access
setMaxSafeLtv 0x9e784426
uint256 _maxSafeLtv
setPositionOwner 0xa4fb4120
address account
setSwapper 0x9c82f2a4
address _swapper
supply 0x35403023
uint256 supplyAmount
supplyAndBorrow 0x62625c3f
uint256 supplyAmount
uint256 borrowAmount
address recipient
withdraw 0x00f714ce
uint256 withdrawAmount
address recipient
returns: uint256

Recent Transactions

No transactions found for this address