Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xbAD003DA1e107f537Ae2f687f5FE7a7aFFe9B241
Balance 0 ETH
Nonce 1
Code Size 15013 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

15013 bytes
0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80635c60da1b116100f9578063c45a015511610097578063dac83f8d11610071578063dac83f8d146104b1578063dc4c90d3146104c4578063f6de0bd2146104d7578063ffb43a5f146104df57600080fd5b8063c45a015514610483578063d784d4261461048b578063d97aea121461049e57600080fd5b80639e5d9942116100d35780639e5d994214610437578063aaf5eb681461044a578063ac6416551461045d578063c3fbb6fd1461047057600080fd5b80635c60da1b146103f357806367e2ba23146103fb5780636ed93dd01461042d57600080fd5b806339ba9f861161016657806340504ba01161014057806340504ba0146103395780634189182e1461036957806345fe329f146103b9578063546ef145146103c157600080fd5b806339ba9f86146102e45780633b99bcee1461030f5780633c9ae2ba1461032257600080fd5b80631cd8d07b116101a25780631cd8d07b1461029857806320d61476146102ab57806323024408146102be57806337fdb36f146102d157600080fd5b80630827b071146101c957806309be15e0146101de578063165b0a9c14610271575b600080fd5b6101dc6101d73660046135bf565b610507565b005b6102306101ec3660046133ee565b60076020526000908152604090205462ffffff808216916301000000810490911690600160301b810464ffffffffff1690600160581b90046001600160a81b031684565b6040805162ffffff958616815294909316602085015264ffffffffff909116918301919091526001600160a81b031660608201526080015b60405180910390f35b6001546102829064ffffffffff1681565b60405164ffffffffff9091168152602001610268565b6101dc6102a63660046133ee565b6108df565b6101dc6102b93660046133ee565b610a6e565b6101dc6102cc3660046133ee565b610b97565b6101dc6102df366004613461565b611101565b6004546102f7906001600160a01b031681565b6040516001600160a01b039091168152602001610268565b6101dc61031d36600461360c565b6111a8565b61032b60035481565b604051908152602001610268565b61034c610347366004613428565b61147e565b604080519315158452602084019290925290820152606001610268565b61039c6103773660046133ee565b60066020526000908152604090205464ffffffffff811690600160281b900460ff1682565b6040805164ffffffffff9093168352901515602083015201610268565b61032b61149a565b6001546103db90600160281b90046001600160701b031681565b6040516001600160701b039091168152602001610268565b6102f76114ce565b60025461041590600160801b90046001600160801b031681565b6040516001600160801b039091168152602001610268565b61032b620f424081565b6101dc6104453660046133ee565b6114dd565b61032b6b033b2e3c9fd0803ce800000081565b600254610415906001600160801b031681565b6101dc61047e366004613503565b611569565b6102f7611611565b6101dc6104993660046133ee565b61161b565b6101dc6104ac366004613558565b6116b0565b6101dc6104bf366004613461565b61170b565b6005546102f7906001600160a01b031681565b61032b611757565b6104f26104ed3660046133ee565b61179d565b60408051928352602083019190915201610268565b61050f611a6c565b3361051981611b34565b60005460011461055c5760405162461bcd60e51b815260206004820152600960248201526813134e9313d0d2d15160ba1b60448201526064015b60405180910390fd5b60026000819055506000336001600160a01b031663ba5d30786040518163ffffffff1660e01b815260040160206040518083038186803b15801561059f57600080fd5b505afa1580156105b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d791906135a6565b905060008364ffffffffff161180156105f05750600081115b80610615575064ffffffffff8316158015610609575080155b80156106155750600087135b6106505760405162461bcd60e51b815260206004820152600c60248201526b13134e90ce9253959053125160a21b6044820152606401610553565b60008761065c83611b98565b61066691906138d3565b90506106723382611bf5565b50506106813389898989611d2f565b87156106985761069861069389613a14565b612009565b60006106a33361209d565b905060006106dc6106d783606001516001600160a81b0316846040015164ffffffffff16426106d291906139d7565b61215a565b611b98565b6106e590613a14565b905064ffffffffff86166107245761071b8161070d84606001516001600160a81b0316611b98565b61071690613a14565b612184565b505050506108d2565b60008a1215610885576000336001600160a01b0316637df1f1b96040518163ffffffff1660e01b815260040160206040518083038186803b15801561076857600080fd5b505afa15801561077c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a0919061340b565b90506107aa61226f565b60405163eaf6e48360e01b81526001600160a01b038381166004830152919091169063eaf6e4839060240160206040518083038186803b1580156107ed57600080fd5b505afa158015610801573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108259190613584565b6108695760405162461bcd60e51b815260206004820152601560248201527426269d219d24a72b20a624a22fa127a92927aba2a960591b6044820152606401610553565b6108833361087e6108798e613a14565b6122e9565b61233b565b505b6000610890336123fc565b90506108cc826108ac85606001516001600160a81b0316611b98565b6108c284606001516001600160a81b0316611b98565b6107169190613998565b50505050505b5050600160005550505050565b6108e7611a6c565b806108f181611b34565b600061096d83846001600160a01b031663ba5d30786040518163ffffffff1660e01b815260040160206040518083038186803b15801561093057600080fd5b505afa158015610944573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061096891906135a6565b611bf5565b915050610978612753565b6001600160a01b0316336001600160a01b031614806109b85750801580156109b857506109a3612795565b6001600160a01b0316336001600160a01b0316145b6109f55760405162461bcd60e51b815260206004820152600e60248201526d0989a74a49892749c9ebe82aaa8960931b6044820152606401610553565b826001600160a01b03166365cd0a4c6040518163ffffffff1660e01b81526004016040805180830381600087803b158015610a2f57600080fd5b505af1158015610a43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6791906136df565b5050505050565b610a76611a6c565b80610a8081611b34565b6000610a8a612753565b6001600160a01b0316336001600160a01b03161490508080610ac45750610aaf612795565b6001600160a01b0316336001600160a01b0316145b610b005760405162461bcd60e51b815260206004820152600d60248201526c0989a749298749c9ebe82aaa89609b1b6044820152606401610553565b826001600160a01b0316630548d2676040518163ffffffff1660e01b81526004016040805180830381600087803b158015610b3a57600080fd5b505af1158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7291906136df565b50508015610b8957610b83836127da565b50505050565b610b83836127ed565b505050565b610b9f611a6c565b600054600114610bdd5760405162461bcd60e51b815260206004820152600960248201526813134e9313d0d2d15160ba1b6044820152606401610553565b6002600055610bea6127fa565b6000816001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b158015610c2557600080fd5b505afa158015610c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5d919061340b565b90506000610c6961226f565b60405163305c9e0560e01b81526e4f545f4c4f414e5f464143544f525960881b60048201526001600160a01b0384811660248301529192509082169063305c9e059060440160206040518083038186803b158015610cc657600080fd5b505afa158015610cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfe9190613584565b610d4a5760405162461bcd60e51b815260206004820152601960248201527f4c4d3a463a494e56414c49445f4c4f414e5f464143544f5259000000000000006044820152606401610553565b60405163140ce5e160e11b81526001600160a01b038481166004830152831690632819cbc29060240160206040518083038186803b158015610d8b57600080fd5b505afa158015610d9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc39190613584565b610e0f5760405162461bcd60e51b815260206004820152601a60248201527f4c4d3a463a494e56414c49445f4c4f414e5f494e5354414e43450000000000006044820152606401610553565b806001600160a01b031663eaf6e483846001600160a01b0316637df1f1b96040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5757600080fd5b505afa158015610e6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8f919061340b565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b158015610ece57600080fd5b505afa158015610ee2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f069190613584565b610f4a5760405162461bcd60e51b815260206004820152601560248201527426269d231d24a72b20a624a22fa127a92927aba2a960591b6044820152606401610553565b6000836001600160a01b031663ba5d30786040518163ffffffff1660e01b815260040160206040518083038186803b158015610f8557600080fd5b505afa158015610f99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fbd91906135a6565b9050806110035760405162461bcd60e51b81526020600482015260146024820152734c4d3a463a4c4f414e5f4e4f545f41435449564560601b6044820152606401610553565b61100d848261233b565b6000846001600160a01b031663b60d42886040518163ffffffff1660e01b8152600401606060405180830381600087803b15801561104a57600080fd5b505af115801561105e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611082919061367f565b505090508181146110cd5760405162461bcd60e51b81526020600482015260156024820152740989a748c748caa9c88929c8ebe9a92a69a82a8869605b1b6044820152606401610553565b6110d961069382611b98565b60006110e4866123fc565b90506108d2600061071683606001516001600160a81b0316611b98565b611109611a6c565b6111116127fa565b8461111b81611b34565b604051632b2d48ad60e21b81526001600160a01b0387169063acb522b49061114d908890889088908890600401613804565b602060405180830381600087803b15801561116757600080fd5b505af115801561117b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119f91906135a6565b50505050505050565b6111b0611a6c565b60006111ba61226f565b90506111c4612795565b6001600160a01b0316336001600160a01b031614156113145760405163fd4c5b3760e01b81526001600160a01b0382169063fd4c5b379061121090339030906000903690600401613782565b60206040518083038186803b15801561122857600080fd5b505afa15801561123c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112609190613584565b6112ac5760405162461bcd60e51b815260206004820152601760248201527f4c4d3a553a494e56414c49445f53434845445f43414c4c0000000000000000006044820152606401610553565b604051635ad5b6f760e11b81526001600160a01b0382169063b5ab6dee906112dd90339060009036906004016137cc565b600060405180830381600087803b1580156112f757600080fd5b505af115801561130b573d6000803e3d6000fd5b505050506113d4565b806001600160a01b031663be7c13f76040518163ffffffff1660e01b815260040160206040518083038186803b15801561134d57600080fd5b505afa158015611361573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611385919061340b565b6001600160a01b0316336001600160a01b0316146113d45760405162461bcd60e51b815260206004820152600c60248201526b0989a74aa749c9ebe82aaa8960a31b6044820152606401610553565b7faaaa7ee6b0c2f4ee1fa7312c7d5b3623a434da5a1a9ce3cb6e629caa23454ab6848484604051611407939291906138b9565b60405180910390a161141761284e565b6001600160a01b031663fe69f7088585856040518463ffffffff1660e01b8152600401611446939291906138b9565b600060405180830381600087803b15801561146057600080fd5b505af1158015611474573d6000803e3d6000fd5b5050505050505050565b600080600061148c8561179d565b600197919650945092505050565b60035460009080156114c5576001546114c09082906106d29064ffffffffff16426139d7565b6114c8565b60005b91505090565b60006114d861287d565b905090565b6114e5611a6c565b6114ed6127fa565b806114f781611b34565b816001600160a01b031663200e53456040518163ffffffff1660e01b81526004016040805180830381600087803b15801561153157600080fd5b505af1158015611545573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8391906136df565b611571611a6c565b61157961284e565b6001600160a01b0316336001600160a01b0316146115cc5760405162461bcd60e51b815260206004820152601060248201526f4c4d3a4d3a4e4f545f464143544f525960801b6044820152606401610553565b6115d78383836128a7565b610b925760405162461bcd60e51b815260206004820152600b60248201526a13134e934e91905253115160aa1b6044820152606401610553565b60006114d861284e565b611623611a6c565b61162b61284e565b6001600160a01b0316336001600160a01b03161461167f5760405162461bcd60e51b81526020600482015260116024820152704c4d3a53493a4e4f545f464143544f525960781b6044820152606401610553565b6001600160a01b03167f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b5050565b6116b8611a6c565b6116c06127fa565b816116ca81611b34565b604051630c7341dd60e31b8152600481018390526001600160a01b0384169063639a0ee8906024016040805180830381600087803b158015610a2f57600080fd5b611713611a6c565b61171b6127fa565b8461172581611b34565b6040516387accaf160e01b81526001600160a01b038716906387accaf19061114d908890889088908890600401613804565b600061176161149a565b60015460025461178a91600160281b90046001600160701b0316906001600160801b0316613914565b6001600160801b03166114d8919061393f565b6000806117a8611a6c565b826117b281611b34565b6005546001600160a01b031633146117fb5760405162461bcd60e51b815260206004820152600c60248201526b4c4d3a54443a4e4f545f504d60a01b6044820152606401610553565b6000611806856127ed565b604051630938afdd60e41b815264ffffffffff82166004820152909150600090819081906001600160a01b0389169063938afdd09060240160a06040518083038186803b15801561185657600080fd5b505afa15801561186a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061188e919061363f565b94505093509350506000886001600160a01b031663ba5d30786040518163ffffffff1660e01b815260040160206040518083038186803b1580156118d157600080fd5b505afa1580156118e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190991906135a6565b9050611915838561393f565b6040516347350e9f60e01b81523060048201529094506000906001600160a01b038b16906347350e9f90602401602060405180830381600087803b15801561195c57600080fd5b505af1158015611970573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199491906135a6565b90506119a38a83878685612920565b909950975060006119b38b61209d565b905060006119e482606001516001600160a81b031683604001518a6119d891906139ee565b64ffffffffff1661215a565b9050611a026119f282611b98565b6119fb90613a14565b6000612184565b611a20611a126106d7838761393f565b611a1b90613a14565b612c79565b611a35611a2c85611b98565b61069390613a14565b5050506001600160a01b039098166000908152600660205260409020805465ffffffffffff19169055509496939550929350505050565b611a7461226f565b604051634c532de160e01b81526001600160e01b03196000351660048201526001600160a01b039190911690634c532de19060240160206040518083038186803b158015611ac157600080fd5b505afa158015611ad5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af99190613584565b15611b325760405162461bcd60e51b815260206004820152600960248201526813134e94105554d15160ba1b6044820152606401610553565b565b6001600160a01b038116600090815260076020526040902054600160301b900464ffffffffff16611b955760405162461bcd60e51b815260206004820152600b60248201526a26269d2727aa2fa627a0a760a91b6044820152606401610553565b50565b60006001600160ff1b03821115611bf15760405162461bcd60e51b815260206004820152601960248201527f4c4d3a55494e543235365f4f4f425f464f525f494e54323536000000000000006044820152606401610553565b5090565b6001600160a01b03821660009081526006602090815260409182902082518084019093525464ffffffffff8116808452600160281b90910460ff1615159183018290529182611c445750611d28565b6001600160a01b0385166000908152600660209081526040808320805465ffffffffffff1916905560078252918290208251608081018452905462ffffff808216835263010000008204169282019290925264ffffffffff600160301b830416928101839052600160581b9091046001600160a81b0316606082018190529091611ce591611a1291611cdb91906119d890896139ee565b6106d7908861393f565b611d25611d0f6106d783606001516001600160a81b03168764ffffffffff16426106d291906139d7565b61071683606001516001600160a81b0316611b98565b50505b9250929050565b6001600160a01b03851660009081526007602090815260408083208151608081018352905462ffffff80821683526301000000820416938201849052600160301b810464ffffffffff1692820192909252600160581b9091046001600160a81b031660608201529190611da3908690612cf4565b90506000611dba86846000015162ffffff16612cf4565b9050600560009054906101000a90046001600160a01b03166001600160a01b031663caf901dd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e0a57600080fd5b505afa158015611e1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e429190613584565b611e5b57611e50858561393f565b935060009450600091505b6000611e67838361393f565b611e7190886139d7565b905060008813611e82576000611e84565b875b60408051828152602081018a9052908101859052606081018890526080810184905260a081018790529098506001600160a01b038a16907f5a3aaae9941b918d74569012f48c308c4044705e7ece73e7834f0f7ffd938b859060c00160405180910390a26004546001600160a01b0316611f0f81611f00612d04565b611f0a858d61393f565b612d49565b611f4f5760405162461bcd60e51b815260206004820152601160248201527004c4d3a4443463a5452414e534645525f5607c1b6044820152606401610553565b611f6581611f5b612795565b611f0a878b61393f565b611fa65760405162461bcd60e51b815260206004820152601260248201527113134e9110d18e9514905394d1915497d41160721b6044820152606401610553565b611fbc81611fb2612d79565b611f0a868a61393f565b611ffd5760405162461bcd60e51b815260206004820152601260248201527113134e9110d18e9514905394d1915497d35560721b6044820152606401610553565b50505050505050505050565b6002547f8e82b75967acb8b37df8c3edc1ef9712a6c32f8a42dbbd441f49be7cab41aee2906120609061205b90849061204a906001600160801b0316611b98565b61205491906138d3565b6000612dbb565b612dd1565b600280546fffffffffffffffffffffffffffffffff19166001600160801b039290921691821790556040519081526020015b60405180910390a150565b604080516080810182526000808252602082018190529181018290526060810191909152506001600160a01b03811660008181526007602081815260408084208151608081018352815462ffffff8082168352630100000082041682860152600160301b810464ffffffffff1682850152600160581b90046001600160a81b031660608201528686529390925290839055519092917f98e0ae8c0ac21cdd4b0a6a2c72737be54e995c54f6ef61b3cd19ec71ed22533b91a2919050565b60006b033b2e3c9fd0803ce80000006121738385613979565b61217d9190613957565b9392505050565b6121b86121b38361204a61219661149a565b6001546106d79190600160281b90046001600160701b031661393f565b612e37565b600160056101000a8154816001600160701b0302191690836001600160701b031602179055506121e742612e9d565b600160006101000a81548164ffffffffff021916908364ffffffffff16021790555061221b6108798261204a600354611b98565b600381905560015460408051928352600160281b9091046001600160701b031660208301527f1e2d80f327730eb6faf6e0ed856016385059a20185b280cb5293a05883ee6f81910160405180910390a15050565b600061227961284e565b6001600160a01b0316633a60339a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156122b157600080fd5b505afa1580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d8919061340b565b600080821215611bf15760405162461bcd60e51b815260206004820152601960248201527f4c4d3a494e543235365f4f4f425f464f525f55494e54323536000000000000006044820152606401610553565b6005546040516324a3335b60e11b8152306004820152602481018390526001600160a01b039091169063494666b690604401600060405180830381600087803b15801561238757600080fd5b505af115801561239b573d6000803e3d6000fd5b50506004546123b792506001600160a01b031690508383612ef4565b6116ac5760405162461bcd60e51b815260206004820152601660248201527513134e941191930e9054141493d59157d1905253115160521b6044820152606401610553565b60408051608081018252600080825260208201819052918101829052606081018290529061242861226f565b600554604051631b14131d60e11b81526001600160a01b039182166004820152911690633628263a9060240160206040518083038186803b15801561246c57600080fd5b505afa158015612480573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a491906135a6565b90506000600560009054906101000a90046001600160a01b03166001600160a01b0316635dd284496040518163ffffffff1660e01b815260040160206040518083038186803b1580156124f657600080fd5b505afa15801561250a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252e91906135a6565b9050600061253c828461393f565b9050620f424081111561255f5761255683620f42406139d7565b9150620f424090505b6000856001600160a01b031663d9ef04596040518163ffffffff1660e01b815260040160206040518083038186803b15801561259a57600080fd5b505afa1580156125ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d291906136c2565b64ffffffffff16905060006125e8878385612fa3565b905060006125f642846139d7565b61260c6b033b2e3c9fd0803ce800000084613979565b6126169190613957565b9050604051806080016040528061262c88613039565b62ffffff16815260200161263f87613039565b62ffffff16815260200161265242612e9d565b64ffffffffff1681526020016126678361308e565b6001600160a81b039081169091526001600160a01b038a166000818152600760209081526040918290208551815487840151888601516060808b0151909916600160581b026affffffffffffffffffffff64ffffffffff909216600160301b029190911665ffffffffffff62ffffff93841663010000000265ffffffffffff19909516939095169290921792909217929092169190911717905581518b81529081018a90529081018790529182018490529198507f948106bee3c901826daa4428037840a48300999c9a8911510b1beb56f2dc02769060800160405180910390a2505050505050919050565b600061275d61226f565b6001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b1580156122b157600080fd5b60055460408051634046af2b60e01b815290516000926001600160a01b031691634046af2b916004808301926020929190829003018186803b1580156122b157600080fd5b60006127e78260016130e7565b92915050565b60006127e78260006130e7565b612802612795565b6001600160a01b0316336001600160a01b031614611b325760405162461bcd60e51b815260206004820152600960248201526813134e9393d517d41160ba1b6044820152606401610553565b60006128787f7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af15490565b919050565b60006128787f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b6000833b806128ba57600091505061217d565b846001600160a01b031684846040516128d4929190613737565b600060405180830381855af49150503d806000811461290f576040519150601f19603f3d011682016040523d82523d6000602084013e612914565b606091505b50909695505050505050565b6001600160a01b03851660009081526007602090815260408083208151608081018352905462ffffff8082168084526301000000830490911694830194909452600160301b810464ffffffffff1692820192909252600160581b9091046001600160a81b031660608201528291829061299a908890612cf4565b905060006129b188846020015162ffffff16612cf4565b905060006129bf828461393f565b6129c9908a6139d7565b905060006129d7848a61393f565b905060006129e589836132b4565b90506129f181836139d7565b96506129fd818a6139d7565b98506000612a168a858f612a11919061393f565b6132b4565b905080612a23858f61393f565b612a2d91906139d7565b9850612a39818b6139d7565b604080518f815260208101879052908101889052606081018d9052909a506001600160a01b038f16907fd6c35cb94fcd8788f1d1e61f1f635e83b26421b57c5db5bb71b275e41588be249060800160405180910390a2604080518b8152602081018390529081018390526001600160a01b038f16907f6fe470ca125c8e736c97af4412c288b2a0417f3f146625f3ff2280449b1e7a8b9060600160405180910390a2612b6b600460009054906101000a90046001600160a01b03168f6001600160a01b0316637df1f1b96040518163ffffffff1660e01b815260040160206040518083038186803b158015612b2d57600080fd5b505afa158015612b41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b65919061340b565b8c612d49565b612bab5760405162461bcd60e51b815260206004820152601160248201527026269d2226231d2a2920a729a322a92fa160791b6044820152606401610553565b600454612bc9906001600160a01b0316612bc3612d04565b83612d49565b612c095760405162461bcd60e51b815260206004820152601160248201527004c4d3a444c463a5452414e534645525f5607c1b6044820152606401610553565b600454612c27906001600160a01b0316612c21612d79565b84612d49565b612c685760405162461bcd60e51b815260206004820152601260248201527113134e9113118e9514905394d1915497d35560721b6044820152606401610553565b505050505050509550959350505050565b7fa6fdea32383e711b89178e80732f32dd09a53fe2cfc2f613a7e5035de5e0bef6612cc861205b8361204a600260109054906101000a90046001600160801b03166001600160801b0316611b98565b600280546001600160801b03908116600160801b93909116928302179055604051908152602001612092565b6000620f42406121738385613979565b600554604080516316f0115b60e01b815290516000926001600160a01b0316916316f0115b916004808301926020929190829003018186803b1580156122b157600080fd5b60006001600160a01b03831615801590612d715750811580612d715750612d718484846132c3565b949350505050565b6000612d8361226f565b6001600160a01b031663a5a276056040518163ffffffff1660e01b815260040160206040518083038186803b1580156122b157600080fd5b6000818313612dca578161217d565b5090919050565b60006001600160801b038213801590612deb575060008212155b611bf15760405162461bcd60e51b815260206004820152601960248201527f4c4d3a494e543235365f4f4f425f464f525f55494e54313238000000000000006044820152606401610553565b60006001600160701b038213801590612e51575060008212155b611bf15760405162461bcd60e51b815260206004820152601960248201527f4c4d3a494e543235365f4f4f425f464f525f55494e54313132000000000000006044820152606401610553565b600064ffffffffff821115611bf15760405162461bcd60e51b815260206004820152601960248201527f4c4d3a55494e543235365f4f4f425f464f525f55494e543430000000000000006044820152606401610553565b6040516001600160a01b038316602482015260006044820181905290612f5a90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526132f6565b612f665750600061217d565b81612f735750600161217d565b6040516001600160a01b038416602482015260448101839052612d7190859063095ea7b360e01b90606401612f23565b604051630938afdd60e41b81526004810183905260009081906001600160a01b0386169063938afdd09060240160a06040518083038186803b158015612fe857600080fd5b505afa158015612ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613020919061363f565b5050509150506130308184613396565b95945050505050565b600062ffffff821115611bf15760405162461bcd60e51b815260206004820152601960248201527f4c4d3a55494e543235365f4f4f425f464f525f55494e543234000000000000006044820152606401610553565b60006001600160a81b03821115611bf15760405162461bcd60e51b815260206004820152601a60248201527f4c4d3a55494e543235365f4f4f425f464f525f55494e543136380000000000006044820152606401610553565b6001600160a01b03821660009081526006602052604090205464ffffffffff168015613112576127e7565b6001600160a01b0383166000908152600760209081526040918290208251608081018452905462ffffff8082168352630100000082041692820192909252600160301b820464ffffffffff1681840152600160581b9091046001600160a81b031660608201528151808301909252908061318b42612e9d565b64ffffffffff80821683528615156020938401526001600160a01b038816600090815260068452604081208551815496909501511515600160281b0265ffffffffffff1990961694909216939093179390931790925560608301519193506131ff9161070d906001600160a81b0316611b98565b6000846001600160a01b031663ba5d30786040518163ffffffff1660e01b815260040160206040518083038186803b15801561323a57600080fd5b505afa15801561324e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327291906135a6565b90506132ac611a1b6132a284606001516001600160a81b0316856040015164ffffffffff16426106d291906139d7565b6106d7908461393f565b505092915050565b6000818310612dca578161217d565b6040516001600160a01b038316602482015260448101829052600090612d7190859063a9059cbb60e01b90606401612f23565b60006001600160a01b0383163b61330f575060006127e7565b6060836001600160a01b0316836040516133299190613747565b6000604051808303816000865af19150503d8060008114613366576040519150601f19603f3d011682016040523d82523d6000602084013e61336b565b606091505b509092509050818015612d71575080511580612d71575080806020019051810190612d719190613584565b60006133a28383612cf4565b61217d90846139d7565b60008083601f8401126133be57600080fd5b50813567ffffffffffffffff8111156133d657600080fd5b602083019150836020828501011115611d2857600080fd5b60006020828403121561340057600080fd5b813561217d81613a47565b60006020828403121561341d57600080fd5b815161217d81613a47565b6000806040838503121561343b57600080fd5b823561344681613a47565b9150602083013561345681613a47565b809150509250929050565b60008060008060006080868803121561347957600080fd5b853561348481613a47565b9450602086013561349481613a47565b935060408601359250606086013567ffffffffffffffff808211156134b857600080fd5b818801915088601f8301126134cc57600080fd5b8135818111156134db57600080fd5b8960208260051b85010111156134f057600080fd5b9699959850939650602001949392505050565b60008060006040848603121561351857600080fd5b833561352381613a47565b9250602084013567ffffffffffffffff81111561353f57600080fd5b61354b868287016133ac565b9497909650939450505050565b6000806040838503121561356b57600080fd5b823561357681613a47565b946020939093013593505050565b60006020828403121561359657600080fd5b8151801515811461217d57600080fd5b6000602082840312156135b857600080fd5b5051919050565b600080600080600060a086880312156135d757600080fd5b8535945060208601359350604086013592506060860135915060808601356135fe81613a5c565b809150509295509295909350565b60008060006040848603121561362157600080fd5b83359250602084013567ffffffffffffffff81111561353f57600080fd5b600080600080600060a0868803121561365757600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b60008060006060848603121561369457600080fd5b8351925060208401516136a681613a5c565b60408501519092506136b781613a5c565b809150509250925092565b6000602082840312156136d457600080fd5b815161217d81613a5c565b600080604083850312156136f257600080fd5b82516136fd81613a5c565b602084015190925061345681613a5c565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8183823760009101908152919050565b6000825160005b81811015613768576020818601810151858301520161374e565b81811115613777576000828501525b509190910192915050565b6001600160a01b03858116825284166020820152694c4d3a5550475241444560b01b60408201526080606082018190526000906137c2908301848661370e565b9695505050505050565b6001600160a01b0384168152694c4d3a5550475241444560b01b6020820152606060408201819052600090613030908301848661370e565b6001600160a01b0385168152602080820185905260606040830181905282018390526000906080600585901b840181019190840186845b878110156138aa57868503607f190183528135368a9003601e1901811261386157600080fd5b8901803567ffffffffffffffff81111561387a57600080fd5b8036038b131561388957600080fd5b613896878288850161370e565b96505050918301919083019060010161383b565b50929998505050505050505050565b83815260406020820152600061303060408301848661370e565b600080821280156001600160ff1b03849003851316156138f5576138f5613a31565b600160ff1b839003841281161561390e5761390e613a31565b50500190565b60006001600160801b0380831681851680830382111561393657613936613a31565b01949350505050565b6000821982111561395257613952613a31565b500190565b60008261397457634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561399357613993613a31565b500290565b60008083128015600160ff1b8501841216156139b6576139b6613a31565b6001600160ff1b03840183138116156139d1576139d1613a31565b50500390565b6000828210156139e9576139e9613a31565b500390565b600064ffffffffff83811690831681811015613a0c57613a0c613a31565b039392505050565b6000600160ff1b821415613a2a57613a2a613a31565b5060000390565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114611b9557600080fd5b64ffffffffff81168114611b9557600080fdfea2646970667358221220688db322fbb27c42a3319d07e951e68b121e403abc92ae93a9d52b35b023332964736f6c63430008070033

Verified Source Code Partial Match

Compiler: v0.8.7+commit.e28d00a7 EVM: london Optimization: Yes (200 runs)
LoanManager.sol 643 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

import { ERC20Helper }           from "../modules/erc20-helper/src/ERC20Helper.sol";
import { IMapleProxyFactory }    from "../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol";
import { MapleProxiedInternals } from "../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol";

import { ILoanManager }                                                from "./interfaces/ILoanManager.sol";
import { IGlobalsLike, ILoanFactoryLike, ILoanLike, IPoolManagerLike } from "./interfaces/Interfaces.sol";

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

/*

    ██╗      ██████╗  █████╗ ███╗   ██╗    ███╗   ███╗ █████╗ ███╗   ██╗ █████╗  ██████╗ ███████╗██████╗
    ██║     ██╔═══██╗██╔══██╗████╗  ██║    ████╗ ████║██╔══██╗████╗  ██║██╔══██╗██╔════╝ ██╔════╝██╔══██╗
    ██║     ██║   ██║███████║██╔██╗ ██║    ██╔████╔██║███████║██╔██╗ ██║███████║██║  ███╗█████╗  ██████╔╝
    ██║     ██║   ██║██╔══██║██║╚██╗██║    ██║╚██╔╝██║██╔══██║██║╚██╗██║██╔══██║██║   ██║██╔══╝  ██╔══██╗
    ███████╗╚██████╔╝██║  ██║██║ ╚████║    ██║ ╚═╝ ██║██║  ██║██║ ╚████║██║  ██║╚██████╔╝███████╗██║  ██║
    ╚══════╝ ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═══╝    ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚═╝  ╚═╝

*/

contract LoanManager is ILoanManager, MapleProxiedInternals, LoanManagerStorage {

    uint256 public override constant HUNDRED_PERCENT = 1e6;   // 100.0000%
    uint256 public override constant PRECISION       = 1e27;

    /**************************************************************************************************************************************/
    /*** Modifiers                                                                                                                      ***/
    /**************************************************************************************************************************************/

    modifier isLoan(address loan_) {
        _revertIfNotLoan(loan_);
        _;
    }

    modifier nonReentrant() {
        require(_locked == 1, "LM:LOCKED");

        _locked = 2;

        _;

        _locked = 1;
    }

    modifier onlyPoolDelegate() {
        _revertIfNotPoolDelegate();
        _;
    }

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

    /**************************************************************************************************************************************/
    /*** Upgradeability Functions                                                                                                       ***/
    /**************************************************************************************************************************************/

    function migrate(address migrator_, bytes calldata arguments_) external override whenNotPaused {
        require(msg.sender == _factory(),        "LM:M:NOT_FACTORY");
        require(_migrate(migrator_, arguments_), "LM:M:FAILED");
    }

    function setImplementation(address implementation_) external override whenNotPaused {
        require(msg.sender == _factory(), "LM:SI:NOT_FACTORY");

        _setImplementation(implementation_);
    }

    function upgrade(uint256 version_, bytes calldata arguments_) external override whenNotPaused {
        IGlobalsLike globals_ = IGlobalsLike(_globals());

        if (msg.sender == _poolDelegate()) {
            require(globals_.isValidScheduledCall(msg.sender, address(this), "LM:UPGRADE", msg.data), "LM:U:INVALID_SCHED_CALL");

            globals_.unscheduleCall(msg.sender, "LM:UPGRADE", msg.data);
        } else {
            require(msg.sender == globals_.securityAdmin(), "LM:U:NO_AUTH");
        }

        emit Upgraded(version_, arguments_);

        IMapleProxyFactory(_factory()).upgradeInstance(version_, arguments_);
    }

    /**************************************************************************************************************************************/
    /*** Loan Funding and Refinancing Functions                                                                                         ***/
    /**************************************************************************************************************************************/

    function fund(address loan_) external override whenNotPaused nonReentrant onlyPoolDelegate {
        address      factory_ = ILoanLike(loan_).factory();
        IGlobalsLike globals_ = IGlobalsLike(_globals());

        require(globals_.isInstanceOf("OT_LOAN_FACTORY", factory_),    "LM:F:INVALID_LOAN_FACTORY");
        require(ILoanFactoryLike(factory_).isLoan(loan_),              "LM:F:INVALID_LOAN_INSTANCE");
        require(globals_.isBorrower(ILoanLike(loan_).borrower()), "LM:F:INVALID_BORROWER");

        uint256 principal_ = ILoanLike(loan_).principal();

        require(principal_ != 0, "LM:F:LOAN_NOT_ACTIVE");

        _prepareFundsForLoan(loan_, principal_);

        ( uint256 fundsLent_, , ) = ILoanLike(loan_).fund();

        require(fundsLent_ == principal_, "LM:F:FUNDING_MISMATCH");

        _updatePrincipalOut(_int256(fundsLent_));

        Payment memory payment_ = _addPayment(loan_);

        _updateInterestAccounting(0, _int256(payment_.issuanceRate));
    }

    function proposeNewTerms(address loan_, address refinancer_, uint256 deadline_, bytes[] calldata calls_)
        external override whenNotPaused onlyPoolDelegate isLoan(loan_)
    {
        ILoanLike(loan_).proposeNewTerms(refinancer_, deadline_, calls_);
    }

    function rejectNewTerms(address loan_, address refinancer_, uint256 deadline_, bytes[] calldata calls_)
        external override whenNotPaused onlyPoolDelegate isLoan(loan_)
    {
        ILoanLike(loan_).rejectNewTerms(refinancer_, deadline_, calls_);
    }

    /**************************************************************************************************************************************/
    /*** Loan Payment Claim Function                                                                                                    ***/
    /**************************************************************************************************************************************/

    function claim(
        int256  principal_,
        uint256 interest_,
        uint256 delegateServiceFee_,
        uint256 platformServiceFee_,
        uint40  nextPaymentDueDate_
    )
        external override whenNotPaused isLoan(msg.sender) nonReentrant
    {
        uint256 principalRemaining_ = ILoanLike(msg.sender).principal();

        // Either a next payment and remaining principal exists, or neither exist and principal is returned.
        require(
            (nextPaymentDueDate_ > 0 && principalRemaining_ > 0) ||                          // First given it's most likely.
            ((nextPaymentDueDate_ == 0) && (principalRemaining_ == 0) && (principal_ > 0)),
            "LM:C:INVALID"
        );

        // Calculate the original principal to correctly account for removing `unrealizedLosses` when removing the impairment.
        uint256 originalPrincipal_ = uint256(_int256(principalRemaining_) + principal_);

        _accountForLoanImpairmentRemoval(msg.sender, originalPrincipal_);

        // Transfer the funds from the loan to the `pool`, `poolDelegate`, and `mapleTreasury`.
        _distributeClaimedFunds(msg.sender, principal_, interest_, delegateServiceFee_, platformServiceFee_);

        // If principal is changing, update `principalOut`.
        // If principal is positive, it is being repaid, so `principalOut` is decremented.
        // If principal is negative, it is being taken from the Pool, so `principalOut` is incremented.
        if (principal_ != 0) {
            _updatePrincipalOut(-principal_);
        }

        // Remove the payment and cache the struct.
        Payment memory claimedPayment_ = _removePayment(msg.sender);

        int256 accountedInterestAdjustment_
            = -_int256(_getIssuance(claimedPayment_.issuanceRate, block.timestamp - claimedPayment_.startDate));

        // If no new payment to track, update accounting and account for discrepancies in paid interest vs accrued interest since the
        // payment's start date, and exit.
        if (nextPaymentDueDate_ == 0) {
            return _updateInterestAccounting(accountedInterestAdjustment_, -_int256(claimedPayment_.issuanceRate));
        }

        if (principal_ < 0) {
            address borrower_ = ILoanLike(msg.sender).borrower();

            require(IGlobalsLike(_globals()).isBorrower(borrower_), "LM:C:INVALID_BORROWER");

            _prepareFundsForLoan(msg.sender, _uint256(-principal_));
        }

        // Track the new payment.
        Payment memory nextPayment_ = _addPayment(msg.sender);

        // Update accounting and account for discrepancies in paid interest vs accrued interest since the payment's start date, and exit.
        _updateInterestAccounting(accountedInterestAdjustment_, _int256(nextPayment_.issuanceRate) - _int256(claimedPayment_.issuanceRate));
    }

    /**************************************************************************************************************************************/
    /*** Loan Call Functions                                                                                                            ***/
    /**************************************************************************************************************************************/

    function callPrincipal(address loan_, uint256 principal_) external override whenNotPaused onlyPoolDelegate isLoan(loan_) {
        ILoanLike(loan_).callPrincipal(principal_);
    }

    function removeCall(address loan_) external override whenNotPaused onlyPoolDelegate isLoan(loan_) {
        ILoanLike(loan_).removeCall();
    }

    /**************************************************************************************************************************************/
    /*** Loan Impairment Functions                                                                                                      ***/
    /**************************************************************************************************************************************/

    function impairLoan(address loan_) external override whenNotPaused isLoan(loan_) {
        bool isGovernor_ = msg.sender == _governor();

        require(isGovernor_ || msg.sender == _poolDelegate(), "LM:IL:NO_AUTH");

        ILoanLike(loan_).impair();

        if (isGovernor_) {
            _accountForLoanImpairmentAsGovernor(loan_);
        } else {
            _accountForLoanImpairment(loan_);
        }
    }

    function removeLoanImpairment(address loan_) external override whenNotPaused isLoan(loan_) {
        ( , bool impairedByGovernor_ ) = _accountForLoanImpairmentRemoval(loan_, ILoanLike(loan_).principal());

        require(msg.sender == _governor() || (!impairedByGovernor_ && msg.sender == _poolDelegate()), "LM:RLI:NO_AUTH");

        ILoanLike(loan_).removeImpairment();
    }

    /**************************************************************************************************************************************/
    /*** Loan Default Functions                                                                                                         ***/
    /**************************************************************************************************************************************/

    function triggerDefault(address loan_, address liquidatorFactory_)
        external override returns (bool liquidationComplete_, uint256 remainingLosses_, uint256 unrecoveredPlatformFees_)
    {
        liquidatorFactory_;  // Silence compiler warning.

        ( remainingLosses_, unrecoveredPlatformFees_ ) = triggerDefault(loan_);

        liquidationComplete_ = true;
    }

    function triggerDefault(address loan_)
        public override whenNotPaused isLoan(loan_) returns (uint256 remainingLosses_, uint256 unrecoveredPlatformFees_)
    {
        require(msg.sender == poolManager, "LM:TD:NOT_PM");

        // Note: Always impair before proceeding, this ensures a consistent approach to reduce the `accountedInterest` for the Loan.
        //       If the Loan is already impaired, this will be a no-op and just return the `impairedDate`.
        //       If the Loan is not impaired, the accountedInterest will be updated to block.timestamp,
        //       which will include the total interest due for the Loan.
        uint40 impairedDate_ = _accountForLoanImpairment(loan_);

        ( , uint256 interest_, uint256 lateInterest_, , uint256 platformServiceFee_ ) = ILoanLike(loan_).getPaymentBreakdown(impairedDate_);

        uint256 principal_ = ILoanLike(loan_).principal();

        interest_ += lateInterest_;

        // Pull any `fundsAsset` in loan into LM.
        uint256 recoveredFunds_ = ILoanLike(loan_).repossess(address(this));

        // Distribute the recovered funds (to treasury, pool, and borrower) and determine the losses, if any, that must still be realized.
        (
            remainingLosses_,
            unrecoveredPlatformFees_
        ) = _distributeLiquidationFunds(loan_, principal_, interest_, platformServiceFee_, recoveredFunds_);

        // Remove the payment and cache the struct.
        Payment memory payment_ = _removePayment(loan_);

        // NOTE: This is the amount of interest accounted for, before the loan's impairment,
        //       that is still in the aggregate `accountedInterest` and offset in `unrealizedLosses`
        //       The original `impairedDate` is always used over the current `impairedDate` on the Loan,
        //       this ensures the interest calculated for `unrealizedLosses` matches the original impairment calculation.
        uint256 accountedImpairedInterest_ = _getIssuance(payment_.issuanceRate, impairedDate_ - payment_.startDate);

        // The payment's interest until the `impairedDate` must be deducted from `accountedInterest`, thus realizing the interest loss.
        // The unrealized losses incurred due to the impairment must be deducted from the global `unrealizedLosses`.
        // The loan's principal must be deducted from `principalOut`, thus realizing the principal loss.
        _updateInterestAccounting(-_int256(accountedImpairedInterest_), 0);
        _updateUnrealizedLosses(-_int256(principal_ + accountedImpairedInterest_));
        _updatePrincipalOut(-_int256(principal_));

        delete impairmentFor[loan_];
    }

    /**************************************************************************************************************************************/
    /*** Internal Functions                                                                                                             ***/
    /**************************************************************************************************************************************/

    function _addPayment(address loan_) internal returns (Payment memory payment_) {
        uint256 platformManagementFeeRate_ = IGlobalsLike(_globals()).platformManagementFeeRate(poolManager);
        uint256 delegateManagementFeeRate_ = IPoolManagerLike(poolManager).delegateManagementFeeRate();
        uint256 managementFeeRate_         = platformManagementFeeRate_ + delegateManagementFeeRate_;

        // NOTE: If combined fee is greater than 100%, then cap delegate fee and clamp management fee.
        if (managementFeeRate_ > HUNDRED_PERCENT) {
            delegateManagementFeeRate_ = HUNDRED_PERCENT - platformManagementFeeRate_;
            managementFeeRate_         = HUNDRED_PERCENT;
        }

        uint256 paymentDueDate_ = ILoanLike(loan_).paymentDueDate();
        uint256 dueInterest_    = _getNetInterest(loan_, paymentDueDate_, managementFeeRate_);

        // NOTE: Can assume `paymentDueDate_ > block.timestamp` and interest at `block.timestamp` is 0 because payments are only added when
        //         - loans are funded, or
        //         - payments are claimed, resulting in a new payment.
        uint256 paymentIssuanceRate_ = (dueInterest_ * PRECISION) / (paymentDueDate_ - block.timestamp);

        paymentFor[loan_] = payment_ = Payment({
            platformManagementFeeRate: _uint24(platformManagementFeeRate_),
            delegateManagementFeeRate: _uint24(delegateManagementFeeRate_),
            startDate:                 _uint40(block.timestamp),
            issuanceRate:              _uint168(paymentIssuanceRate_)
        });

        emit PaymentAdded(
            loan_,
            platformManagementFeeRate_,
            delegateManagementFeeRate_,
            paymentDueDate_,
            paymentIssuanceRate_
        );
    }

    function _accountForLoanImpairment(address loan_, bool isGovernor_) internal returns (uint40 impairedDate_) {
        impairedDate_ = impairmentFor[loan_].impairedDate;

        // NOTE: Impairing an already-impaired loan simply updates the `dateImpaired` of the loan, which can push the due date further,
        //       however, the `impairedDate` in the struct should not be updated since it defines the moment when accounting for the loan's
        //       payment was paused, and is needed to restore accounting for the eventual removal of the impairment, or the default.
        if (impairedDate_ != 0) return impairedDate_;

        Payment memory payment_ = paymentFor[loan_];

        impairmentFor[loan_] = Impairment(impairedDate_ = _uint40(block.timestamp), isGovernor_);

        // Account for all interest until now (including this payment's), then remove payment's `issuanceRate` from global `issuanceRate`.
        _updateInterestAccounting(0, -_int256(payment_.issuanceRate));

        uint256 principal_ = ILoanLike(loan_).principal();

        // Add the payment's entire interest until now (negating above), and the loan's principal, to unrealized losses.
        _updateUnrealizedLosses(_int256(principal_ + _getIssuance(payment_.issuanceRate, block.timestamp - payment_.startDate)));
    }

    function _accountForLoanImpairment(address loan_) internal returns (uint40 impairedDate_) {
        impairedDate_ = _accountForLoanImpairment(loan_, false);
    }

    function _accountForLoanImpairmentAsGovernor(address loan_) internal returns (uint40 impairedDate_) {
        impairedDate_ = _accountForLoanImpairment(loan_, true);
    }

    function _accountForLoanImpairmentRemoval(address loan_, uint256 originalPrincipal_) internal returns (uint40 impairedDate_, bool impairedByGovernor_) {
        Impairment memory impairment_ = impairmentFor[loan_];

        impairedDate_       = impairment_.impairedDate;
        impairedByGovernor_ = impairment_.impairedByGovernor;

        if (impairedDate_ == 0) return ( impairedDate_, impairedByGovernor_ );

        delete impairmentFor[loan_];

        Payment memory payment_ = paymentFor[loan_];

        // Subtract the payment's entire interest until it's impairment date, and the loan's principal, from unrealized losses.
        _updateUnrealizedLosses(-_int256(originalPrincipal_ + _getIssuance(payment_.issuanceRate, impairedDate_ - payment_.startDate)));

        // Account for all interest until now, adjusting for payment's interest between its impairment date and now,
        // then add payment's `issuanceRate` to the global `issuanceRate`.
        // NOTE: Upon impairment, for payment's interest between its start date and its impairment date were accounted for.
        _updateInterestAccounting(
            _int256(_getIssuance(payment_.issuanceRate, block.timestamp - impairedDate_)),
            _int256(payment_.issuanceRate)
        );
    }

    function _removePayment(address loan_) internal returns (Payment memory payment_) {
        payment_ = paymentFor[loan_];

        delete paymentFor[loan_];

        emit PaymentRemoved(loan_);
    }

    function _updateInterestAccounting(int256 accountedInterestAdjustment_, int256 issuanceRateAdjustment_) internal {
        // NOTE: Order of operations is important as `accruedInterest()` depends on the pre-adjusted `issuanceRate` and `domainStart`.
        accountedInterest = _uint112(_max(_int256(accountedInterest + accruedInterest()) + accountedInterestAdjustment_, 0));
        domainStart       = _uint40(block.timestamp);
        issuanceRate      = _uint256(_max(_int256(issuanceRate) + issuanceRateAdjustment_, 0));

        emit AccountingStateUpdated(issuanceRate, accountedInterest);
    }

    function _updatePrincipalOut(int256 principalOutAdjustment_) internal {
        emit PrincipalOutUpdated(principalOut = _uint128(_max(_int256(principalOut) + principalOutAdjustment_, 0)));
    }

    function _updateUnrealizedLosses(int256 lossesAdjustment_) internal {
        emit UnrealizedLossesUpdated(unrealizedLosses = _uint128(_max(_int256(unrealizedLosses) + lossesAdjustment_, 0)));
    }

    /**************************************************************************************************************************************/
    /*** Funds Distribution Functions                                                                                                   ***/
    /**************************************************************************************************************************************/

    function _distributeClaimedFunds(
        address loan_,
        int256  principal_,
        uint256 interest_,
        uint256 delegateServiceFee_,
        uint256 platformServiceFee_
    )
        internal
    {
        Payment memory payment_ = paymentFor[loan_];

        uint256 delegateManagementFee_ = _getRatedAmount(interest_, payment_.delegateManagementFeeRate);
        uint256 platformManagementFee_ = _getRatedAmount(interest_, payment_.platformManagementFeeRate);

        // If the coverage is not sufficient move the delegate service fee to the platform and remove the delegate management fee.
        if (!IPoolManagerLike(poolManager).hasSufficientCover()) {
            platformServiceFee_ += delegateServiceFee_;

            delegateServiceFee_    = 0;
            delegateManagementFee_ = 0;
        }

        uint256 netInterest_ = interest_ - (platformManagementFee_ + delegateManagementFee_);

        principal_ = principal_ > int256(0) ? principal_ : int256(0);

        emit ClaimedFundsDistributed(
            loan_,
            uint256(principal_),
            interest_,
            delegateManagementFee_,
            delegateServiceFee_,
            platformManagementFee_,
            platformServiceFee_
        );

        address fundsAsset_ = fundsAsset;

        require(_transfer(fundsAsset_, _pool(),         uint256(principal_) + netInterest_),           "LM:DCF:TRANSFER_P");
        require(_transfer(fundsAsset_, _poolDelegate(), delegateServiceFee_ + delegateManagementFee_), "LM:DCF:TRANSFER_PD");
        require(_transfer(fundsAsset_, _treasury(),     platformServiceFee_ + platformManagementFee_), "LM:DCF:TRANSFER_MT");
    }

    function _distributeLiquidationFunds(
        address loan_,
        uint256 principal_,
        uint256 interest_,
        uint256 platformServiceFee_,
        uint256 recoveredFunds_
    )
        internal returns (uint256 remainingLosses_, uint256 unrecoveredPlatformFees_)
    {
        Payment memory payment_ = paymentFor[loan_];

        uint256 platformManagementFee_ = _getRatedAmount(interest_, payment_.platformManagementFeeRate);
        uint256 delegateManagementFee_ = _getRatedAmount(interest_, payment_.delegateManagementFeeRate);

        uint256 netInterest_ = interest_ - (platformManagementFee_ + delegateManagementFee_);
        uint256 platformFee_ = platformServiceFee_ + platformManagementFee_;

        uint256 toTreasury_ = _min(recoveredFunds_, platformFee_);

        unrecoveredPlatformFees_ = platformFee_ - toTreasury_;

        recoveredFunds_ -= toTreasury_;

        uint256 toPool_ = _min(recoveredFunds_, principal_ + netInterest_);

        remainingLosses_ = principal_ + netInterest_ - toPool_;

        recoveredFunds_ -= toPool_;

        emit ExpectedClaim(loan_, principal_, netInterest_, platformManagementFee_, platformServiceFee_);

        emit LiquidatedFundsDistributed(loan_, recoveredFunds_, toPool_, toTreasury_);

        // NOTE: Cannot cache `fundsAsset` due to "Stack too deep" issue.
        require(_transfer(fundsAsset, ILoanLike(loan_).borrower(), recoveredFunds_), "LM:DLF:TRANSFER_B");
        require(_transfer(fundsAsset, _pool(),                     toPool_),         "LM:DLF:TRANSFER_P");
        require(_transfer(fundsAsset, _treasury(),                 toTreasury_),     "LM:DLF:TRANSFER_MT");
    }

    function _prepareFundsForLoan(address loan_, uint256 amount_) internal {
        // Request funds from pool manager.
        IPoolManagerLike(poolManager).requestFunds(address(this), amount_);

        // Approve the loan to use these funds.
        require(ERC20Helper.approve(fundsAsset, loan_, amount_), "LM:PFFL:APPROVE_FAILED");
    }

    function _transfer(address asset_, address to_, uint256 amount_) internal returns (bool success_) {
        success_ = (to_ != address(0)) && ((amount_ == 0) || ERC20Helper.transfer(asset_, to_, amount_));
    }

    /**************************************************************************************************************************************/
    /*** Internal Loan Accounting Helper Functions                                                                                      ***/
    /**************************************************************************************************************************************/

    function _getIssuance(uint256 issuanceRate_, uint256 interval_) internal pure returns (uint256 issuance_) {
        issuance_ = (issuanceRate_ * interval_) / PRECISION;
    }

    function _getNetInterest(address loan_, uint256 timestamp_, uint256 managementFeeRate_) internal view returns (uint256 netInterest_) {
        ( , uint256 interest_, , , ) = ILoanLike(loan_).getPaymentBreakdown(timestamp_);

        netInterest_ = _getNetInterest(interest_, managementFeeRate_);
    }

    function _getNetInterest(uint256 interest_, uint256 feeRate_) internal pure returns (uint256 netInterest_) {
        // NOTE: This ensures that `netInterest_ == interest_ - fee_`, since absolutes are subtracted, not rates.
        netInterest_ = interest_ - _getRatedAmount(interest_, feeRate_);
    }

    function _getRatedAmount(uint256 amount_, uint256 rate_) internal pure returns (uint256 ratedAmount_) {
        ratedAmount_ = (amount_ * rate_) / HUNDRED_PERCENT;
    }

    /**************************************************************************************************************************************/
    /*** Loan Manager View Functions                                                                                                    ***/
    /**************************************************************************************************************************************/

    function accruedInterest() public view override returns (uint256 accruedInterest_) {
        uint256 issuanceRate_ = issuanceRate;

        accruedInterest_ = issuanceRate_ == 0 ? 0 : _getIssuance(issuanceRate_, block.timestamp - domainStart);
    }

    function assetsUnderManagement() public view virtual override returns (uint256 assetsUnderManagement_) {
        assetsUnderManagement_ = principalOut + accountedInterest + accruedInterest();
    }

    /**************************************************************************************************************************************/
    /*** Protocol Address View Functions                                                                                                ***/
    /**************************************************************************************************************************************/

    function factory() external view override returns (address factory_) {
        factory_ = _factory();
    }

    function implementation() external view override returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**************************************************************************************************************************************/
    /*** Internal View Functions                                                                                                        ***/
    /**************************************************************************************************************************************/

    function _globals() internal view returns (address globals_) {
        globals_ = IMapleProxyFactory(_factory()).mapleGlobals();
    }

    function _governor() internal view returns (address governor_) {
        governor_ = IGlobalsLike(_globals()).governor();
    }

    function _pool() internal view returns (address pool_) {
        pool_ = IPoolManagerLike(poolManager).pool();
    }

    function _poolDelegate() internal view returns (address poolDelegate_) {
        poolDelegate_ = IPoolManagerLike(poolManager).poolDelegate();
    }

    function _revertIfNotLoan(address loan_) internal view {
        require(paymentFor[loan_].startDate != 0, "LM:NOT_LOAN");
    }

    function _revertIfNotPoolDelegate() internal view {
        require(msg.sender == _poolDelegate(), "LM:NOT_PD");
    }

    function _revertIfPaused() internal view {
        require(!IGlobalsLike(_globals()).isFunctionPaused(msg.sig), "LM:PAUSED");
    }

    function _treasury() internal view returns (address treasury_) {
        treasury_ = IGlobalsLike(_globals()).mapleTreasury();
    }

    /**************************************************************************************************************************************/
    /*** Internal Pure Utility Functions                                                                                                ***/
    /**************************************************************************************************************************************/

    function _int256(uint256 input_) internal pure returns (int256 output_) {
        require(input_ <= uint256(type(int256).max), "LM:UINT256_OOB_FOR_INT256");
        output_ = int256(input_);
    }

    function _max(int256 a_, int256 b_) internal pure returns (int256 maximum_) {
        maximum_ = a_ > b_ ? a_ : b_;
    }

    function _min(uint256 a_, uint256 b_) internal pure returns (uint256 minimum_) {
        minimum_ = a_ < b_ ? a_ : b_;
    }

    function _uint24(uint256 input_) internal pure returns (uint24 output_) {
        require(input_ <= type(uint24).max, "LM:UINT256_OOB_FOR_UINT24");
        output_ = uint24(input_);
    }

    function _uint40(uint256 input_) internal pure returns (uint40 output_) {
        require(input_ <= type(uint40).max, "LM:UINT256_OOB_FOR_UINT40");
        output_ = uint40(input_);
    }

    function _uint112(int256 input_) internal pure returns (uint112 output_) {
        require(input_ <= int256(uint256(type(uint112).max)) && input_ >= 0, "LM:INT256_OOB_FOR_UINT112");
        output_ = uint112(uint256(input_));
    }

    function _uint128(int256 input_) internal pure returns (uint128 output_) {
        require(input_ <= int256(uint256(type(uint128).max)) && input_ >= 0, "LM:INT256_OOB_FOR_UINT128");
        output_ = uint128(uint256(input_));
    }

    function _uint168(uint256 input_) internal pure returns (uint168 output_) {
        require(input_ <= type(uint168).max, "LM:UINT256_OOB_FOR_UINT168");
        output_ = uint168(input_);
    }

    function _uint168(int256 input_) internal pure returns (uint168 output_) {
        require(input_ <= int256(uint256(type(uint168).max)) && input_ >= 0, "LM:INT256_OOB_FOR_UINT168");
        output_ = uint168(uint256(input_));
    }

    function _uint256(int256 input_) internal pure returns (uint256 output_) {
        require(input_ >= 0, "LM:INT256_OOB_FOR_UINT256");
        output_ = uint256(input_);
    }

}
LoanManagerStorage.sol 36 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

import { ILoanManagerStorage } from "./interfaces/ILoanManagerStorage.sol";

abstract contract LoanManagerStorage is ILoanManagerStorage {

    struct Impairment {
        uint40 impairedDate;        // Slot 1: uint40 - Until year 36,812.
        bool   impairedByGovernor;  //         bool
    }

    struct Payment {
        uint24  platformManagementFeeRate;  // Slot 1: uint24  - max = 1.6e7 (1600%)
        uint24  delegateManagementFeeRate;  //         uint24  - max = 1.6e7 (1600%)
        uint40  startDate;                  //         uint40  - Until year 36,812.
        uint168 issuanceRate;               //         uint168 - max = 3.7e50 (3.2e10 * 1e18 / day)
    }

    uint256 internal _locked;  // Used when checking for reentrancy.

    uint40  public override domainStart;        // Slot 1: uint40  - Until year 36,812.
    uint112 public override accountedInterest;  //         uint112 - max = 5.1e33
    uint128 public override principalOut;       // Slot 2: uint128 - max = 3.4e38
    uint128 public override unrealizedLosses;   //         uint128 - max = 3.4e38
    uint256 public override issuanceRate;       // Slot 3: uint256 - max = 1.1e77

    // NOTE: Addresses below uints to preserve full storage slots
    address public override fundsAsset;
    address public override poolManager;

    mapping(address => Impairment) public override impairmentFor;

    mapping(address => Payment) public override paymentFor;

}
Interfaces.sol 105 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

interface IGlobalsLike {

    function canDeploy(address caller_) external view returns (bool canDeploy_);

    function governor() external view returns (address governor_);

    function isBorrower(address borrower_) external view returns (bool isBorrower_);

    function isFunctionPaused(bytes4 sig_) external view returns (bool isFunctionPaused_);

    function isInstanceOf(bytes32 instanceId, address instance_) external view returns (bool isInstance_);

    function isValidScheduledCall(address caller_, address contract_, bytes32 functionId_, bytes calldata callData_)
        external view returns (bool isValid_);

    function mapleTreasury() external view returns (address mapleTreasury_);

    function platformManagementFeeRate(address poolManager_) external view returns (uint256 platformManagementFeeRate_);

    function securityAdmin() external view returns (address securityAdmin_);

    function unscheduleCall(address caller_, bytes32 functionId_, bytes calldata callData_) external;

}

interface IMapleProxyFactoryLike {

    function isInstance(address instance_) external returns (bool isInstance_);

    function mapleGlobals() external returns (address globals_);

}

interface ILoanFactoryLike {

    function isLoan(address loan_) external view returns (bool isLoan_);

}

interface ILoanLike {

    function borrower() external view returns (address borrower_);

    function callPrincipal(uint256 principalToReturn_) external returns (uint40 paymentDueDate_, uint40 defaultDate_);

    function factory() external view returns (address factory_);

    function fund() external returns (uint256 fundsLent_, uint40 paymentDueDate_, uint40 defaultDate_);

    function impair() external returns (uint40 paymentDueDate_, uint40 defaultDate_);

    function paymentDueDate() external view returns (uint40 paymentDueDate_);

    function getPaymentBreakdown(uint256 paymentTimestamp_)
        external view
        returns (
            uint256 principal_,
            uint256 interest_,
            uint256 lateInterest_,
            uint256 delegateServiceFee_,
            uint256 platformServiceFee_
        );

    function principal() external view returns (uint256 principal_);

    function proposeNewTerms(
        address refinancer_,
        uint256 deadline_,
        bytes[] calldata calls_
    ) external returns (bytes32 refinanceCommitment_);

    function rejectNewTerms(
        address refinancer_,
        uint256 deadline_,
        bytes[] calldata calls_
    ) external returns (bytes32 refinanceCommitment_);

    function removeCall() external returns (uint40 paymentDueDate_, uint40 defaultDate_);

    function removeImpairment() external returns (uint40 paymentDueDate_, uint40 defaultDate_);

    function repossess(address destination_) external returns (uint256 fundsRepossessed_);

}

interface IPoolManagerLike {

    function asset() external view returns (address asset_);

    function delegateManagementFeeRate() external view returns (uint256 delegateManagementFeeRate_);

    function factory() external view returns (address factory_);

    function hasSufficientCover() external view returns (bool hasSufficientCover_);

    function pool() external view returns (address pool_);

    function poolDelegate() external view returns (address poolDelegate_);

    function requestFunds(address destination_, uint256 principal_) external;

}
ILoanManager.sol 223 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

import { IMapleProxied } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxied.sol";

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

interface ILoanManager is IMapleProxied, ILoanManagerStorage {

    /**************************************************************************************************************************************/
    /*** Events                                                                                                                         ***/
    /**************************************************************************************************************************************/

    /**
     *  @dev   Emitted when the accounting state of the loan manager is updated.
     *  @param issuanceRate_      New value for the issuance rate.
     *  @param accountedInterest_ The amount of accounted interest.
     */
    event AccountingStateUpdated(uint256 issuanceRate_, uint112 accountedInterest_);

    /**
     *  @dev   Funds have been claimed and distributed to the Pool, Pool Delegate, and Maple Treasury.
     *  @param loan_                  The address of the loan contract.
     *  @param principal_             The amount of principal paid.
     *  @param netInterest_           The amount of net interest paid.
     *  @param delegateManagementFee_ The amount of delegate management fees paid.
     *  @param delegateServiceFee_    The amount of delegate service fees paid.
     *  @param platformManagementFee_ The amount of platform management fees paid.
     *  @param platformServiceFee_    The amount of platform service fees paid.
     */
    event ClaimedFundsDistributed(
        address indexed loan_,
        uint256 principal_,
        uint256 netInterest_,
        uint256 delegateManagementFee_,
        uint256 delegateServiceFee_,
        uint256 platformManagementFee_,
        uint256 platformServiceFee_
    );

    /**
     *  @dev   Funds that were expected to be claimed and distributed to the Pool and Maple Treasury.
     *  @param loan_                  The address of the loan contract.
     *  @param principal_             The amount of principal that was expected to be paid.
     *  @param netInterest_           The amount of net interest that was expected to be paid.
     *  @param platformManagementFee_ The amount of platform management fees that were expected to be paid.
     *  @param platformServiceFee_    The amount of platform service fees that were expected to paid.
     */
    event ExpectedClaim(
        address indexed loan_,
        uint256 principal_,
        uint256 netInterest_,
        uint256 platformManagementFee_,
        uint256 platformServiceFee_
    );

    /**
     *  @dev   Funds that were liquidated and distributed to the Pool, Maple Treasury, and Borrower.
     *  @param loan_       The address of the loan contract that defaulted and was liquidated.
     *  @param toBorrower_ The amount of recovered funds transferred to the Borrower.
     *  @param toPool_     The amount of recovered funds transferred to the Pool.
     *  @param toTreasury_ The amount of recovered funds transferred to the Treasury.
     */
    event LiquidatedFundsDistributed(address indexed loan_, uint256 toBorrower_, uint256 toPool_, uint256 toTreasury_);

    /**
     *  @dev   Emitted when a payment is added to the LoanManager payments mapping.
     *  @param loan_                      The address of the loan.
     *  @param platformManagementFeeRate_ The amount of platform management rate that will be used for the payment distribution.
     *  @param delegateManagementFeeRate_ The amount of delegate management rate that will be used for the payment distribution.
     *  @param paymentDueDate_            The due date of the payment.
     *  @param issuanceRate_              The issuance of the payment, 1e27 precision.
     */
    event PaymentAdded(
        address indexed loan_,
        uint256 platformManagementFeeRate_,
        uint256 delegateManagementFeeRate_,
        uint256 paymentDueDate_,
        uint256 issuanceRate_
    );

    /**
     *  @dev   Emitted when a payment is removed from the LoanManager payments mapping.
     *  @param loan_ The address of the loan.
     */
    event PaymentRemoved(address indexed loan_);

    /**
     *  @dev   Emitted when principal out is updated
     *  @param principalOut_ The new value for principal out.
     */
    event PrincipalOutUpdated(uint128 principalOut_);

    /**
     *  @dev   Emitted when unrealized losses is updated.
     *  @param unrealizedLosses_ The new value for unrealized losses.
     */
    event UnrealizedLossesUpdated(uint128 unrealizedLosses_);

    /**************************************************************************************************************************************/
    /*** External Functions                                                                                                             ***/
    /**************************************************************************************************************************************/

    // NOTE: setPendingLender and acceptPendingLender were not implemented in the LoanManager even though they exist on the Loan
    //       contract. This is because the Loan will support this functionality always, but it was not deemed necessary for the
    //       LoanManager to support this functionality.

    /**
     *  @dev   Calls a loan.
     *  @param loan_      Loan to be called.
     *  @param principal_ Amount of principal to call the Loan with.
     */
    function callPrincipal(address loan_, uint256 principal_) external;

    /**
     *  @dev   Called by loans when payments are made, updating the accounting.
     *  @param principal_          The difference in principal. Positive if net principal change moves funds into pool, negative if it moves
     *                             funds out of pool.
     *  @param interest_           The amount of interest paid.
     *  @param platformServiceFee_ The amount of platform service fee paid.
     *  @param delegateServiceFee_ The amount of delegate service fee paid.
     *  @param paymentDueDate_     The new payment due date.
     */
    function claim(
        int256  principal_,
        uint256 interest_,
        uint256 delegateServiceFee_,
        uint256 platformServiceFee_,
        uint40  paymentDueDate_
    ) external;

    /**
     *  @dev   Funds a new loan.
     *  @param loan_ Loan to be funded.
     */
    function fund(address loan_) external;

    /**
     *  @dev   Triggers the impairment of a loan.
     *  @param loan_ Loan to trigger the loan impairment.
     */
    function impairLoan(address loan_) external;

    /**
     *  @dev   Proposes new terms for a loan.
     *  @param loan_       The loan to propose new changes to.
     *  @param refinancer_ The refinancer to use in the refinance.
     *  @param deadline_   The deadline by which the borrower must accept the new terms.
     *  @param calls_      The array of calls to be made to the refinancer.
     */
    function proposeNewTerms(address loan_, address refinancer_, uint256 deadline_, bytes[] calldata calls_) external;

    /**
     *  @dev   Reject/cancel proposed new terms for a loan.
     *  @param loan_       The loan with the proposed new changes.
     *  @param refinancer_ The refinancer to use in the refinance.
     *  @param deadline_   The deadline by which the borrower must accept the new terms.
     *  @param calls_      The array of calls to be made to the refinancer.
     */
    function rejectNewTerms(address loan_, address refinancer_, uint256 deadline_, bytes[] calldata calls_) external;

    /**
     *  @dev   Removes a loan call.
     *  @param loan_ Loan to remove call for.
     */
    function removeCall(address loan_) external;

    /**
     *  @dev   Removes the loan impairment for a loan.
     *  @param loan_ Loan to remove the loan impairment.
     */
    function removeLoanImpairment(address loan_) external;

    /**
     *  @dev    Triggers the default of a loan. Different interface for PM to accommodate vs FT-LM.
     *  @param  loan_                    Loan to trigger the default.
     *  @param  liquidatorFactory_       Address of the liquidator factory (ignored for open-term loans).
     *  @return liquidationComplete_     If the liquidation is complete (always true for open-term loans)
     *  @return remainingLosses_         The amount of un-recovered principal and interest (net of management fees).
     *  @return unrecoveredPlatformFees_ The amount of un-recovered platform fees.
     */
    function triggerDefault(
        address loan_,
        address liquidatorFactory_
    ) external returns (bool liquidationComplete_, uint256 remainingLosses_, uint256 unrecoveredPlatformFees_);

    /**
     *  @dev    Triggers the default of a loan.
     *  @param  loan_                    Loan to trigger the default.
     *  @return remainingLosses_         The amount of un-recovered principal and interest (net of management fees).
     *  @return unrecoveredPlatformFees_ The amount of un-recovered platform fees.
     */
    function triggerDefault(address loan_) external returns (uint256 remainingLosses_, uint256 unrecoveredPlatformFees_);

    /**************************************************************************************************************************************/
    /*** View Functions                                                                                                                 ***/
    /**************************************************************************************************************************************/

    /**
     *  @dev    Returns the value considered as the hundred percent.
     *  @return hundredPercent_ The value considered as the hundred percent.
     */
    function HUNDRED_PERCENT() external returns (uint256 hundredPercent_);

    /**
     *  @dev    Returns the precision used for the contract.
     *  @return precision_ The precision used for the contract.
     */
    function PRECISION() external returns (uint256 precision_);

    /**
     *  @dev    Gets the amount of accrued interest up until this point in time.
     *  @return accruedInterest_ The amount of accrued interest up until this point in time.
     */
    function accruedInterest() external view returns (uint256 accruedInterest_);

    /**
     *  @dev    Gets the amount of assets under the management of the contract.
     *  @return assetsUnderManagement_ The amount of assets under the management of the contract.
     */
    function assetsUnderManagement() external view returns (uint256 assetsUnderManagement_);

}
ERC20Helper.sol 43 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

import { IERC20Like } from "./interfaces/IERC20Like.sol";

/**
 * @title Small Library to standardize erc20 token interactions.
 */
library ERC20Helper {

    /**************************************************************************************************************************************/
    /*** Internal Functions                                                                                                             ***/
    /**************************************************************************************************************************************/

    function transfer(address token_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transfer.selector, to_, amount_));
    }

    function transferFrom(address token_, address from_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transferFrom.selector, from_, to_, amount_));
    }

    function approve(address token_, address spender_, uint256 amount_) internal returns (bool success_) {
        // If setting approval to zero fails, return false.
        if (!_call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, uint256(0)))) return false;

        // If `amount_` is zero, return true as the previous step already did this.
        if (amount_ == uint256(0)) return true;

        // Return the result of setting the approval to `amount_`.
        return _call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, amount_));
    }

    function _call(address token_, bytes memory data_) private returns (bool success_) {
        if (token_.code.length == uint256(0)) return false;

        bytes memory returnData;
        ( success_, returnData ) = token_.call(data_);

        return success_ && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
    }

}
ILoanManagerStorage.sol 71 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;

interface ILoanManagerStorage {

    /**
     *  @dev    Gets the amount of accounted interest.
     *  @return accountedInterest_ The amount of accounted interest.
     */
    function accountedInterest() external view returns (uint112 accountedInterest_);

    /**
     *  @dev    Gets the timestamp of the domain start.
     *  @return domainStart_ The timestamp of the domain start.
     */
    function domainStart() external view returns (uint40 domainStart_);

    /**
     *  @dev    Gets the address of the funds asset.
     *  @return fundsAsset_ The address of the funds asset.
     */
    function fundsAsset() external view returns (address fundsAsset_);

    /**
     *  @dev    Gets the information for an impairment.
     *  @param  loan_              The address of the loan.
     *  @return impairedDate       The date the impairment was triggered.
     *  @return impairedByGovernor True if the impairment was triggered by the governor.
     */
    function impairmentFor(address loan_) external view returns (uint40 impairedDate, bool impairedByGovernor);

    /**
     *  @dev    Gets the current issuance rate.
     *  @return issuanceRate_ The value for the issuance rate.
     */
    function issuanceRate() external view returns (uint256 issuanceRate_);

    /**
     *  @dev    Gets the information for a payment.
     *  @param  loan_                     The address of the loan.
     *  @return platformManagementFeeRate The value for the platform management fee rate.
     *  @return delegateManagementFeeRate The value for the delegate management fee rate.
     *  @return startDate                 The start date of the payment.
     *  @return issuanceRate              The issuance rate for the loan.
     */
    function paymentFor(address loan_) external view returns (
        uint24  platformManagementFeeRate,
        uint24  delegateManagementFeeRate,
        uint40  startDate,
        uint168 issuanceRate
    );

    /**
     *  @dev    Gets the address of the pool manager.
     *  @return poolManager_ The address of the pool manager.
     */
    function poolManager() external view returns (address poolManager_);

    /**
     *  @dev    Gets the amount of principal out.
     *  @return principalOut_ The amount of principal out.
     */
    function principalOut() external view returns (uint128 principalOut_);

    /**
     *  @dev    Returns the amount unrealized losses.
     *  @return unrealizedLosses_ Amount of unrealized losses.
     */
    function unrealizedLosses() external view returns (uint128 unrealizedLosses_);

}
IERC20Like.sol 13 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title Interface of the ERC20 standard as needed by ERC20Helper.
interface IERC20Like {

    function approve(address spender_, uint256 amount_) external returns (bool success_);

    function transfer(address recipient_, uint256 amount_) external returns (bool success_);

    function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

}
MapleProxiedInternals.sol 7 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { ProxiedInternals } from "../modules/proxy-factory/contracts/ProxiedInternals.sol";

/// @title A Maple implementation that is to be proxied, will need MapleProxiedInternals.
abstract contract MapleProxiedInternals is ProxiedInternals { }
IMapleProxied.sol 24 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IProxied } from "../../modules/proxy-factory/contracts/interfaces/IProxied.sol";

/// @title A Maple implementation that is to be proxied, must implement IMapleProxied.
interface IMapleProxied is IProxied {

    /**
     *  @dev   The instance was upgraded.
     *  @param toVersion_ The new version of the loan.
     *  @param arguments_ The upgrade arguments, if any.
     */
    event Upgraded(uint256 toVersion_, bytes arguments_);

    /**
     *  @dev   Upgrades a contract implementation to a specific version.
     *         Access control logic critical since caller can force a selfdestruct via a malicious `migrator_` which is delegatecalled.
     *  @param toVersion_ The version to upgrade to.
     *  @param arguments_ Some encoded arguments to use for the upgrade.
     */
    function upgrade(uint256 toVersion_, bytes calldata arguments_) external;

}
IMapleProxyFactory.sol 189 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { IDefaultImplementationBeacon } from "../../modules/proxy-factory/contracts/interfaces/IDefaultImplementationBeacon.sol";

/// @title A Maple factory for Proxy contracts that proxy MapleProxied implementations.
interface IMapleProxyFactory is IDefaultImplementationBeacon {

    /**************************************************************************************************************************************/
    /*** Events                                                                                                                         ***/
    /**************************************************************************************************************************************/

    /**
     *  @dev   A default version was set.
     *  @param version_ The default version.
     */
    event DefaultVersionSet(uint256 indexed version_);

    /**
     *  @dev   A version of an implementation, at some address, was registered, with an optional initializer.
     *  @param version_               The version registered.
     *  @param implementationAddress_ The address of the implementation.
     *  @param initializer_           The address of the initializer, if any.
     */
    event ImplementationRegistered(uint256 indexed version_, address indexed implementationAddress_, address indexed initializer_);

    /**
     *  @dev   A proxy contract was deployed with some initialization arguments.
     *  @param version_                 The version of the implementation being proxied by the deployed proxy contract.
     *  @param instance_                The address of the proxy contract deployed.
     *  @param initializationArguments_ The arguments used to initialize the proxy contract, if any.
     */
    event InstanceDeployed(uint256 indexed version_, address indexed instance_, bytes initializationArguments_);

    /**
     *  @dev   A instance has upgraded by proxying to a new implementation, with some migration arguments.
     *  @param instance_           The address of the proxy contract.
     *  @param fromVersion_        The initial implementation version being proxied.
     *  @param toVersion_          The new implementation version being proxied.
     *  @param migrationArguments_ The arguments used to migrate, if any.
     */
    event InstanceUpgraded(address indexed instance_, uint256 indexed fromVersion_, uint256 indexed toVersion_, bytes migrationArguments_);

    /**
     *  @dev   The MapleGlobals was set.
     *  @param mapleGlobals_ The address of a Maple Globals contract.
     */
    event MapleGlobalsSet(address indexed mapleGlobals_);

    /**
     *  @dev   An upgrade path was disabled, with an optional migrator contract.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     */
    event UpgradePathDisabled(uint256 indexed fromVersion_, uint256 indexed toVersion_);

    /**
     *  @dev   An upgrade path was enabled, with an optional migrator contract.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     *  @param migrator_    The address of the migrator, if any.
     */
    event UpgradePathEnabled(uint256 indexed fromVersion_, uint256 indexed toVersion_, address indexed migrator_);

    /**************************************************************************************************************************************/
    /*** State Variables                                                                                                                ***/
    /**************************************************************************************************************************************/

    /**
     *  @dev The default version.
     */
    function defaultVersion() external view returns (uint256 defaultVersion_);

    /**
     *  @dev The address of the MapleGlobals contract.
     */
    function mapleGlobals() external view returns (address mapleGlobals_);

    /**
     *  @dev    Whether the upgrade is enabled for a path from a version to another version.
     *  @param  toVersion_   The initial version.
     *  @param  fromVersion_ The destination version.
     *  @return allowed_     Whether the upgrade is enabled.
     */
    function upgradeEnabledForPath(uint256 toVersion_, uint256 fromVersion_) external view returns (bool allowed_);

    /**************************************************************************************************************************************/
    /*** State Changing Functions                                                                                                       ***/
    /**************************************************************************************************************************************/

    /**
     *  @dev    Deploys a new instance proxying the default implementation version, with some initialization arguments.
     *          Uses a nonce and `msg.sender` as a salt for the CREATE2 opcode during instantiation to produce deterministic addresses.
     *  @param  arguments_ The initialization arguments to use for the instance deployment, if any.
     *  @param  salt_      The salt to use in the contract creation process.
     *  @return instance_  The address of the deployed proxy contract.
     */
    function createInstance(bytes calldata arguments_, bytes32 salt_) external returns (address instance_);

    /**
     *  @dev   Enables upgrading from a version to a version of an implementation, with an optional migrator.
     *         Only the Governor can call this function.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     *  @param migrator_    The address of the migrator, if any.
     */
    function enableUpgradePath(uint256 fromVersion_, uint256 toVersion_, address migrator_) external;

    /**
     *  @dev   Disables upgrading from a version to a version of a implementation.
     *         Only the Governor can call this function.
     *  @param fromVersion_ The starting version of the upgrade path.
     *  @param toVersion_   The destination version of the upgrade path.
     */
    function disableUpgradePath(uint256 fromVersion_, uint256 toVersion_) external;

    /**
     *  @dev   Registers the address of an implementation contract as a version, with an optional initializer.
     *         Only the Governor can call this function.
     *  @param version_               The version to register.
     *  @param implementationAddress_ The address of the implementation.
     *  @param initializer_           The address of the initializer, if any.
     */
    function registerImplementation(uint256 version_, address implementationAddress_, address initializer_) external;

    /**
     *  @dev   Sets the default version.
     *         Only the Governor can call this function.
     *  @param version_ The implementation version to set as the default.
     */
    function setDefaultVersion(uint256 version_) external;

    /**
     *  @dev   Sets the Maple Globals contract.
     *         Only the Governor can call this function.
     *  @param mapleGlobals_ The address of a Maple Globals contract.
     */
    function setGlobals(address mapleGlobals_) external;

    /**
     *  @dev   Upgrades the calling proxy contract's implementation, with some migration arguments.
     *  @param toVersion_ The implementation version to upgrade the proxy contract to.
     *  @param arguments_ The migration arguments, if any.
     */
    function upgradeInstance(uint256 toVersion_, bytes calldata arguments_) external;

    /**************************************************************************************************************************************/
    /*** View Functions                                                                                                                 ***/
    /**************************************************************************************************************************************/

    /**
     *  @dev    Returns the deterministic address of a potential proxy, given some arguments and salt.
     *  @param  arguments_       The initialization arguments to be used when deploying the proxy.
     *  @param  salt_            The salt to be used when deploying the proxy.
     *  @return instanceAddress_ The deterministic address of a potential proxy.
     */
    function getInstanceAddress(bytes calldata arguments_, bytes32 salt_) external view returns (address instanceAddress_);

    /**
     *  @dev    Returns the address of an implementation version.
     *  @param  version_        The implementation version.
     *  @return implementation_ The address of the implementation.
     */
    function implementationOf(uint256 version_) external view returns (address implementation_);

    /**
     *  @dev    Returns if a given address has been deployed by this factory/
     *  @param  instance_   The address to check.
     *  @return isInstance_ A boolean indication if the address has been deployed by this factory.
     */
    function isInstance(address instance_) external view returns (bool isInstance_);

    /**
     *  @dev    Returns the address of a migrator contract for a migration path (from version, to version).
     *          If oldVersion_ == newVersion_, the migrator is an initializer.
     *  @param  oldVersion_ The old version.
     *  @param  newVersion_ The new version.
     *  @return migrator_   The address of a migrator contract.
     */
    function migratorForPath(uint256 oldVersion_, uint256 newVersion_) external view returns (address migrator_);

    /**
     *  @dev    Returns the version of an implementation contract.
     *  @param  implementation_ The address of an implementation contract.
     *  @return version_        The version of the implementation contract.
     */
    function versionOf(address implementation_) external view returns (uint256 version_);

}
ProxiedInternals.sol 50 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

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

/// @title An implementation that is to be proxied, will need ProxiedInternals.
abstract contract ProxiedInternals is SlotManipulatable {

    /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.factory') - 1`.
    bytes32 private constant FACTORY_SLOT = bytes32(0x7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af1);

    /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`.
    bytes32 private constant IMPLEMENTATION_SLOT = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);

    /// @dev Delegatecalls to a migrator contract to manipulate storage during an initialization or migration.
    function _migrate(address migrator_, bytes calldata arguments_) internal virtual returns (bool success_) {
        uint256 size;

        assembly {
            size := extcodesize(migrator_)
        }

        if (size == uint256(0)) return false;

        ( success_, ) = migrator_.delegatecall(arguments_);
    }

    /// @dev Sets the factory address in storage.
    function _setFactory(address factory_) internal virtual returns (bool success_) {
        _setSlotValue(FACTORY_SLOT, bytes32(uint256(uint160(factory_))));
        return true;
    }

    /// @dev Sets the implementation address in storage.
    function _setImplementation(address implementation_) internal virtual returns (bool success_) {
        _setSlotValue(IMPLEMENTATION_SLOT, bytes32(uint256(uint160(implementation_))));
        return true;
    }

    /// @dev Returns the factory address.
    function _factory() internal view virtual returns (address factory_) {
        return address(uint160(uint256(_getSlotValue(FACTORY_SLOT))));
    }

    /// @dev Returns the implementation address.
    function _implementation() internal view virtual returns (address implementation_) {
        return address(uint160(uint256(_getSlotValue(IMPLEMENTATION_SLOT))));
    }

}
SlotManipulatable.sol 22 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

abstract contract SlotManipulatable {

    function _getReferenceTypeSlot(bytes32 slot_, bytes32 key_) internal pure returns (bytes32 value_) {
        return keccak256(abi.encodePacked(key_, slot_));
    }

    function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_) {
        assembly {
            value_ := sload(slot_)
        }
    }

    function _setSlotValue(bytes32 slot_, bytes32 value_) internal {
        assembly {
            sstore(slot_, value_)
        }
    }

}
IProxied.sol 31 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title An implementation that is to be proxied, must implement IProxied.
interface IProxied {

    /**
     *  @dev The address of the proxy factory.
     */
    function factory() external view returns (address factory_);

    /**
     *  @dev The address of the implementation contract being proxied.
     */
    function implementation() external view returns (address implementation_);

    /**
     *  @dev   Modifies the proxy's implementation address.
     *  @param newImplementation_ The address of an implementation contract.
     */
    function setImplementation(address newImplementation_) external;

    /**
     *  @dev   Modifies the proxy's storage by delegate-calling a migrator contract with some arguments.
     *         Access control logic critical since caller can force a selfdestruct via a malicious `migrator_` which is delegatecalled.
     *  @param migrator_  The address of a migrator contract.
     *  @param arguments_ Some encoded arguments to use for the migration.
     */
    function migrate(address migrator_, bytes calldata arguments_) external;

}
IDefaultImplementationBeacon.sol 10 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title An beacon that provides a default implementation for proxies, must implement IDefaultImplementationBeacon.
interface IDefaultImplementationBeacon {

    /// @dev The address of an implementation for proxies.
    function defaultImplementation() external view returns (address defaultImplementation_);

}

Read Contract

HUNDRED_PERCENT 0x6ed93dd0 → uint256
PRECISION 0xaaf5eb68 → uint256
accountedInterest 0x546ef145 → uint112
accruedInterest 0x45fe329f → uint256
assetsUnderManagement 0xf6de0bd2 → uint256
domainStart 0x165b0a9c → uint40
factory 0xc45a0155 → address
fundsAsset 0x39ba9f86 → address
impairmentFor 0x4189182e → uint40, bool
implementation 0x5c60da1b → address
issuanceRate 0x3c9ae2ba → uint256
paymentFor 0x09be15e0 → uint24, uint24, uint40, uint168
poolManager 0xdc4c90d3 → address
principalOut 0xac641655 → uint128
unrealizedLosses 0x67e2ba23 → uint128

Write Contract 13 functions

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

callPrincipal 0xd97aea12
address loan_
uint256 principal_
claim 0x0827b071
int256 principal_
uint256 interest_
uint256 delegateServiceFee_
uint256 platformServiceFee_
uint40 nextPaymentDueDate_
fund 0x23024408
address loan_
impairLoan 0x20d61476
address loan_
migrate 0xc3fbb6fd
address migrator_
bytes arguments_
proposeNewTerms 0xdac83f8d
address loan_
address refinancer_
uint256 deadline_
bytes[] calls_
rejectNewTerms 0x37fdb36f
address loan_
address refinancer_
uint256 deadline_
bytes[] calls_
removeCall 0x9e5d9942
address loan_
removeLoanImpairment 0x1cd8d07b
address loan_
setImplementation 0xd784d426
address implementation_
triggerDefault 0x40504ba0
address loan_
address liquidatorFactory_
returns: bool, uint256, uint256
triggerDefault 0xffb43a5f
address loan_
returns: uint256, uint256
upgrade 0x3b99bcee
uint256 version_
bytes arguments_

Recent Transactions

No transactions found for this address