Address: 0xC02D8A4afafc0E25E950f4a6D160C4E6FC8FbB81
Balance (XRP): 0 XRP
Bytecode: 0x608060405234801561001057600080fd5b50600436106100f55760003560e01c806357c89a7d11610097578063969948691161006657806396994869146102b1578063995ed99f146102d1578063bdf950c9146102f1578063c5ae59341461031157600080fd5b806357c89a7d14610230578063595642191461025157806375d80e90146102715780637dd8f6d91461029157600080fd5b80632b2d5ed6116100d35780632b2d5ed614610192578063416405d7146101b257806343c811cc146101f05780634b70d84b1461021057600080fd5b80630972bf8b146100fa578063158eca8b146101235780631ea6374114610143575b600080fd5b61010d610108366004612d2e565b610331565b60405161011a9190612dc4565b60405180910390f35b610136610131366004612e12565b610401565b60405161011a9190612efb565b610156610151366004612f0a565b610ba1565b60405161011a919081518152602080830151908201526040808301516001600160a01b0316908201526060918201519181019190915260800190565b6101a56101a0366004612f4a565b610f02565b60405161011a9190612f8b565b6101c56101c0366004612feb565b610fd7565b604080518251815260208084015190820152918101516001600160a01b03169082015260600161011a565b6102036101fe366004613143565b611178565b60405161011a91906131a4565b61022361021e366004612f4a565b61131a565b60405161011a9190613209565b61024361023e366004612feb565b6113d8565b60405190815260200161011a565b61026461025f36600461324c565b611763565b60405161011a91906132b0565b61028461027f3660046132f2565b6118ff565b60405161011a9190613452565b6102a461029f366004612feb565b611b60565b60405161011a919061358c565b6102c46102bf3660046132f2565b611c9a565b60405161011a91906135fb565b6102e46102df366004613143565b611ee4565b60405161011a919061371c565b6103046102ff366004612feb565b612085565b60405161011a9190613780565b61032461031f366004612e12565b6123ce565b60405161011a919061378e565b6060826000816001600160401b0381111561034e5761034e613024565b60405190808252806020026020018201604052801561038757816020015b610374612b59565b81526020019060019003908161036c5790505b50905060005b828110156103f5576103c58787838181106103aa576103aa6137ae565b90506020020160208101906103bf9190612e12565b86612085565b8282815181106103d7576103d76137ae565b602002602001018190525080806103ed906137da565b91505061038d565b509150505b9392505050565b610409612b98565b6000826001600160a01b031663bd6d894d6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561044b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046f91906137f3565b90506000836001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d5919061380c565b604051638e8f294b60e01b81526001600160a01b038681166004830152919250600091829190841690638e8f294b906024016040805180830381865afa158015610523573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610547919061383e565b915091506000806105d9886001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801561058f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105b791908101906138d8565b604051806040016040528060048152602001630c68aa8960e31b815250612538565b156105ea575060009050601261071d565b6000889050806001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561062d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610651919061380c565b9250806001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610691573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b5919061380c565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107169190613930565b60ff169150505b60008061072a878b612591565b9150915060008080896001600160a01b0316634a58443260e01b8e60405160200161076491906001600160a01b0391909116815260200190565b60408051601f1981840301815290829052610782929160200161394b565b60408051601f198184030181529082905261079c9161397c565b6000604051808303816000865af19150503d80600081146107d9576040519150601f19603f3d011682016040523d82523d6000602084013e6107de565b606091505b5091509150811561080057808060200190518101906107fd91906137f3565b92505b6040518061022001604052808e6001600160a01b031681526020018c81526020018e6001600160a01b031663ae9d70b06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561085f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088391906137f3565b81526020018e6001600160a01b031663f8f9da286040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ea91906137f3565b81526020018e6001600160a01b031663173b99046040518163ffffffff1660e01b8152600401602060405180830381865afa15801561092d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095191906137f3565b81526020018e6001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610994573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b891906137f3565b81526020018e6001600160a01b0316638f840ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109fb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1f91906137f3565b81526020018e6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8691906137f3565b81526020018e6001600160a01b0316633b1d21a26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ac9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aed91906137f3565b81526020018a15158152602001898152602001886001600160a01b031681526020018e6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b719190613930565b60ff168152602001878152602001868152602001858152602001848152509b505050505050505050505050919050565b610bd56040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b6040516370a0823160e01b81526001600160a01b038381166004830152600091908616906370a0823190602401602060405180830381865afa158015610c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4391906137f3565b6040516374d7814960e11b81526001600160a01b0385811660048301529192509085169063e9af029290602401600060405180830381600087803b158015610c8a57600080fd5b505af1158015610c9e573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b03868116600483015260009350881691506370a0823190602401602060405180830381865afa158015610ceb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0f91906137f3565b60405163331faf7160e21b81526001600160a01b03868116600483015291925060009187169063cc7ebdc490602401602060405180830381865afa158015610d5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7f91906137f3565b90506000610db482846040518060400160405280600e81526020016d1cdd5b4818dbdb5c081d1bdd185b60921b815250612814565b90506000610de882866040518060400160405280600d81526020016c1cdd5888185b1b1bd8d85d1959609a1b81525061284e565b60408051608081018252878152905163b4b5ea5760e01b81526001600160a01b038a8116600483015292935090916020830191908c169063b4b5ea5790602401602060405180830381865afa158015610e45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6991906139af565b6001600160601b03168152604051632c3e6f0f60e11b81526001600160a01b038a811660048301526020909201918c169063587cde1e90602401602060405180830381865afa158015610ec0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ee4919061380c565b6001600160a01b031681526020019190915298975050505050505050565b6060816000816001600160401b03811115610f1f57610f1f613024565b604051908082528060200260200182016040528015610f6457816020015b6040805180820190915260008082526020820152815260200190600190039081610f3d5790505b50905060005b82811015610fcc57610f9c868683818110610f8757610f876137ae565b905060200201602081019061031f9190612e12565b828281518110610fae57610fae6137ae565b60200260200101819052508080610fc4906137da565b915050610f6a565b509150505b92915050565b6110046040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b6040805160608101918290526370a0823160e01b9091526001600160a01b038381166064830152819085166370a0823160848301602060405180830381865afa158015611055573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107991906137f3565b815260405163b4b5ea5760e01b81526001600160a01b03858116600483015260209092019186169063b4b5ea5790602401602060405180830381865afa1580156110c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110eb91906139af565b6001600160601b03168152604051632c3e6f0f60e11b81526001600160a01b03858116600483015260209092019186169063587cde1e90602401602060405180830381865afa158015611142573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611166919061380c565b6001600160a01b031690529392505050565b80516060906000816001600160401b0381111561119757611197613024565b6040519080825280602002602001820160405280156111e957816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816111b55790505b50905060005b828110156103f5576000876001600160a01b031663e23a9a5287848151811061121a5761121a6137ae565b6020026020010151896040518363ffffffff1660e01b81526004016112529291909182526001600160a01b0316602082015260400190565b606060405180830381865afa15801561126f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129391906139ca565b905060405180608001604052808784815181106112b2576112b26137ae565b60200260200101518152602001826000015115158152602001826020015160ff16815260200182604001516001600160601b03168152508383815181106112fb576112fb6137ae565b6020026020010181905250508080611312906137da565b9150506111ef565b6060816000816001600160401b0381111561133757611337613024565b60405190808252806020026020018201604052801561137057816020015b61135d612b98565b8152602001906001900390816113555790505b50905060005b82811015610fcc576113a8868683818110611393576113936137ae565b90506020020160208101906101319190612e12565b8282815181106113ba576113ba6137ae565b602002602001018190525080806113d0906137da565b915050611376565b60006113e2612c30565b600080856001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611423573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611447919061380c565b604051632aff3bff60e21b81526001600160a01b03878116600483015291925060009188169063abfceffc90602401600060405180830381865afa158015611493573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114bb9190810190613a18565b905060005b81518110156117275760008282815181106114dd576114dd6137ae565b6020908102919091010151604051638e8f294b60e01b81526001600160a01b0380831660048301529192506000918b1690638e8f294b906024016040805180830381865afa158015611533573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611557919061383e565b6040516361bfb47160e11b81526001600160a01b038c81166004830152919350908416915063c37f68e290602401608060405180830381865afa1580156115a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115c69190613aa6565b60808b015260608a01526040890152955085156115ed576000975050505050505050610fd1565b604080516020808201835283825260c08a0191909152815190810182526080890151815260e08901525163fc57d4df60e01b81526001600160a01b03838116600483015286169063fc57d4df90602401602060405180830381865afa15801561165a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167e91906137f3565b60a0880181905260000361169c576000975050505050505050610fd1565b604080516020810190915260a0880151815261010088015260c087015160e08801516116d6916116cb91612888565b886101000151612888565b6101208801819052604088015188516116f09291906128d0565b87526101008701516060880151602089015161170d9291906128d0565b60208801525081905061171f816137da565b9150506114c0565b50600084602001511161173c57600019611758565b83516117589061174e90612710613adc565b85602001516128f1565b979650505050505050565b60606000826001600160401b0381111561177f5761177f613024565b6040519080825280602002602001820160405280156117c457816020015b604080518082019091526000808252602082015281526020019060019003908161179d5790505b50905060005b838110156118f55760405180604001604052808686848181106117ef576117ef6137ae565b90506020020160208101906118049190613af3565b63ffffffff168152602001886001600160a01b031663782d6fe189898987818110611831576118316137ae565b90506020020160208101906118469190613af3565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015263ffffffff166024820152604401602060405180830381865afa158015611895573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b991906139af565b6001600160601b03168152508282815181106118d7576118d76137ae565b602002602001018190525080806118ed906137da565b9150506117ca565b5095945050505050565b60606000826001600160401b0381111561191b5761191b613024565b6040519080825280602002602001820160405280156119cb57816020015b6119b8604051806101c001604052806000815260200160006001600160a01b03168152602001600081526020016060815260200160608152602001606081526020016060815260200160008152602001600081526020016000815260200160008152602001600081526020016000151581526020016000151581525090565b8152602001906001900390816119395790505b50905060005b83811015611b5757600080600080896001600160a01b031663328dd9828a8a88818110611a0057611a006137ae565b905060200201356040518263ffffffff1660e01b8152600401611a2591815260200190565b600060405180830381865afa158015611a42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a6a9190810190613c87565b9350935093509350604051806101c001604052806000815260200160006001600160a01b0316815260200160008152602001858152602001848152602001838152602001828152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160001515815250868681518110611af957611af96137ae565b6020026020010181905250611b40868681518110611b1957611b196137ae565b60200260200101518b8b8b89818110611b3457611b346137ae565b905060200201356128fd565b505050508080611b4f906137da565b9150506119d1565b50949350505050565b611b8460405180606001604052806060815260200160008152602001600081525090565b604051635ec88c7960e01b81526001600160a01b0383811660048301526000918291829190871690635ec88c7990602401606060405180830381865afa158015611bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf69190613d94565b92509250925082600014611c0957600080fd5b604080516060810191829052632aff3bff60e21b9091526001600160a01b0386811660648301528190881663abfceffc60848301600060405180830381865afa158015611c5a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611c829190810190613a18565b81526020810193909352604090920152949350505050565b60606000826001600160401b03811115611cb657611cb6613024565b604051908082528060200260200182016040528015611d5f57816020015b611d4c604051806101a001604052806000815260200160006001600160a01b031681526020016000815260200160608152602001606081526020016060815260200160608152602001600081526020016000815260200160008152602001600081526020016000151581526020016000151581525090565b815260200190600190039081611cd45790505b50905060005b83811015611b5757600080600080896001600160a01b031663328dd9828a8a88818110611d9457611d946137ae565b905060200201356040518263ffffffff1660e01b8152600401611db991815260200190565b600060405180830381865afa158015611dd6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611dfe9190810190613c87565b9350935093509350604051806101a001604052806000815260200160006001600160a01b03168152602001600081526020018581526020018481526020018381526020018281526020016000815260200160008152602001600081526020016000815260200160001515815260200160001515815250868681518110611e8657611e866137ae565b6020026020010181905250611ecd868681518110611ea657611ea66137ae565b60200260200101518b8b8b89818110611ec157611ec16137ae565b905060200201356129e8565b505050508080611edc906137da565b915050611d65565b80516060906000816001600160401b03811115611f0357611f03613024565b604051908082528060200260200182016040528015611f5557816020015b604080516080810182526000808252602080830182905292820181905260608201528252600019909201910181611f215790505b50905060005b828110156103f5576000876001600160a01b031663e23a9a52878481518110611f8657611f866137ae565b6020026020010151896040518363ffffffff1660e01b8152600401611fbe9291909182526001600160a01b0316602082015260400190565b606060405180830381865afa158015611fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fff9190613dc2565b9050604051806080016040528087848151811061201e5761201e6137ae565b6020026020010151815260200182600001511515815260200182602001511515815260200182604001516001600160601b0316815250838381518110612066576120666137ae565b602002602001018190525050808061207d906137da565b915050611f5b565b61208d612b59565b6040516370a0823160e01b81526001600160a01b038381166004830152600091908516906370a0823190602401602060405180830381865afa1580156120d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120fb91906137f3565b6040516305eff7ef60e21b81526001600160a01b0385811660048301529192506000918616906317bfdfbc906024016020604051808303816000875af1158015612149573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216d91906137f3565b604051633af9e66960e01b81526001600160a01b038681166004830152919250600091871690633af9e669906024016020604051808303816000875af11580156121bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121df91906137f3565b9050600080612225886001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801561058f573d6000803e3d6000fd5b1561223f5750506001600160a01b03851680319031612391565b60008890506000816001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015612284573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a8919061380c565b6040516370a0823160e01b81526001600160a01b038b81166004830152919250908216906370a0823190602401602060405180830381865afa1580156122f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061231691906137f3565b604051636eb1769f60e11b81526001600160a01b038b811660048301528c811660248301529195509082169063dd62ed3e90604401602060405180830381865afa158015612368573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238c91906137f3565b925050505b6040805160c0810182526001600160a01b039990991689526020890195909552938701929092526060860152608085015260a08401525090919050565b60408051808201909152600080825260208201526000826001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612422573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612446919061380c565b90506000816001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015612488573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ac919061380c565b6040805180820182526001600160a01b03808816808352925163fc57d4df60e01b815260048101939093529293509160208301919084169063fc57d4df90602401602060405180830381865afa15801561250a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252e91906137f3565b9052949350505050565b60008160405160200161254b919061397c565b6040516020818303038152906040528051906020012083604051602001612572919061397c565b6040516020818303038152906040528051906020012014905092915050565b60008060008080866001600160a01b0316636aa875b560e01b876040516020016125ca91906001600160a01b0391909116815260200190565b60408051601f19818403018152908290526125e8929160200161394b565b60408051601f19818403018152908290526126029161397c565b6000604051808303816000865af19150503d806000811461263f576040519150601f19603f3d011682016040523d82523d6000602084013e612644565b606091505b50915091508115612666578080602001905181019061266391906137f3565b92505b604080516001600160a01b038881166020830152600092839283928c16916303d290cf60e61b910160408051601f19818403018152908290526126ac929160200161394b565b60408051601f19818403018152908290526126c69161397c565b6000604051808303816000865af19150503d8060008114612703576040519150601f19603f3d011682016040523d82523d6000602084013e612708565b606091505b5091509150811561272a578080602001905181019061272791906137f3565b92505b841580612735575081155b1561280357604080516001600160a01b038b811660208301526000928392918e1691631d7b33d760e01b910160408051601f198184030181529082905261277f929160200161394b565b60408051601f19818403018152908290526127999161397c565b6000604051808303816000865af19150503d80600081146127d6576040519150601f19603f3d011682016040523d82523d6000602084013e6127db565b606091505b5091509150811561280057808060200190518101906127fa91906137f3565b94508497505b50505b5093955093505050505b9250929050565b6000806128218486613df3565b90508285821015611b575760405162461bcd60e51b81526004016128459190613e06565b60405180910390fd5b600081848411156128725760405162461bcd60e51b81526004016128459190613e06565b50600061287f8486613e19565b95945050505050565b6040805160208101909152600081526040518060200160405280670de0b6b3a76400006128bd86600001518660000151612b01565b6128c79190613e2c565b90529392505050565b6000806128dd8585612b0d565b905061287f6128eb82612b35565b84612b4d565b60006103fa8284613e2c565b60405163013cf08b60e01b8152600481018290526000906001600160a01b0384169063013cf08b9060240161014060405180830381865afa158015612946573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296a9190613e4e565b918452506020808201516001600160a01b03169084015260408082015190840152606081015160e08085019190915260808201516101008086019190915260a08301516101208087019190915260c0840151610140870152918301516101608601528201511515610180850152015115156101a09092019190915250565b600080600080600080600080896001600160a01b031663013cf08b8a6040518263ffffffff1660e01b8152600401612a2291815260200190565b61012060405180830381865afa158015612a40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a649190613ee5565b9850985098509850985098509850985050888b6000018181525050878b602001906001600160a01b031690816001600160a01b031681525050868b6040018181525050858b60e0018181525050848b610100018181525050838b610120018181525050828b610140018181525050818b610160019015159081151581525050808b6101800190151590811515815250505050505050505050505050565b60006103fa8284613adc565b60408051602081019091526000815260405180602001604052806128c7856000015185612b01565b8051600090610fd190670de0b6b3a764000090613e2c565b60006103fa8284613df3565b6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b60405180610220016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000151581526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806101400160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001612c7c6040518060200160405280600081525090565b8152602001612c976040518060200160405280600081525090565b8152602001612cb26040518060200160405280600081525090565b8152602001612ccd6040518060200160405280600081525090565b905290565b60008083601f840112612ce457600080fd5b5081356001600160401b03811115612cfb57600080fd5b6020830191508360208260051b850101111561280d57600080fd5b6001600160a01b0381168114612d2b57600080fd5b50565b600080600060408486031215612d4357600080fd5b83356001600160401b03811115612d5957600080fd5b612d6586828701612cd2565b9094509250506020840135612d7981612d16565b809150509250925092565b80516001600160a01b031682526020808201519083015260408082015190830152606080820151908301526080808201519083015260a090810151910152565b6020808252825182820181905260009190848201906040850190845b81811015612e0657612df3838551612d84565b9284019260c09290920191600101612de0565b50909695505050505050565b600060208284031215612e2457600080fd5b81356103fa81612d16565b80516001600160a01b031682526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e083015261010080820151818401525061012080820151612ea28285018215159052565b50506101408181015190830152610160808201516001600160a01b03169083015261018080820151908301526101a080820151908301526101c080820151908301526101e0808201519083015261020090810151910152565b6102208101610fd18284612e2f565b600080600060608486031215612f1f57600080fd5b8335612f2a81612d16565b92506020840135612f3a81612d16565b91506040840135612d7981612d16565b60008060208385031215612f5d57600080fd5b82356001600160401b03811115612f7357600080fd5b612f7f85828601612cd2565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b82811015612fde57612fce84835180516001600160a01b03168252602090810151910152565b9284019290850190600101612fa8565b5091979650505050505050565b60008060408385031215612ffe57600080fd5b823561300981612d16565b9150602083013561301981612d16565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561305c5761305c613024565b60405290565b60405161014081016001600160401b038111828210171561305c5761305c613024565b604051601f8201601f191681016001600160401b03811182821017156130ad576130ad613024565b604052919050565b60006001600160401b038211156130ce576130ce613024565b5060051b60200190565b600082601f8301126130e957600080fd5b813560206130fe6130f9836130b5565b613085565b82815260059290921b8401810191818101908684111561311d57600080fd5b8286015b848110156131385780358352918301918301613121565b509695505050505050565b60008060006060848603121561315857600080fd5b833561316381612d16565b9250602084013561317381612d16565b915060408401356001600160401b0381111561318e57600080fd5b61319a868287016130d8565b9150509250925092565b602080825282518282018190526000919060409081850190868401855b82811015612fde57815180518552868101511515878601528581015160ff16868601526060908101516001600160601b031690850152608090930192908501906001016131c1565b6020808252825182820181905260009190848201906040850190845b81811015612e0657613238838551612e2f565b928401926102209290920191600101613225565b6000806000806060858703121561326257600080fd5b843561326d81612d16565b9350602085013561327d81612d16565b925060408501356001600160401b0381111561329857600080fd5b6132a487828801612cd2565b95989497509550505050565b602080825282518282018190526000919060409081850190868401855b82811015612fde578151805185528601518685015292840192908501906001016132cd565b60008060006040848603121561330757600080fd5b833561331281612d16565b925060208401356001600160401b0381111561332d57600080fd5b61333986828701612cd2565b9497909650939450505050565b600081518084526020808501945080840160005b8381101561337f5781516001600160a01b03168752958201959082019060010161335a565b509495945050505050565b600081518084526020808501945080840160005b8381101561337f5781518752958201959082019060010161339e565b60005b838110156133d55781810151838201526020016133bd565b50506000910152565b600081518084526133f68160208601602086016133ba565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b85811015612fde5782840389526134408483516133de565b98850198935090840190600101613428565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561357e57888303603f19018552815180518452878101516001600160a01b038116898601526101c09050878201518886015260608083015182828801526134c683880182613346565b92505050608080830151868303828801526134e1838261338a565b9250505060a080830151868303828801526134fc838261340a565b9250505060c08083015186830382880152613517838261340a565b60e085810151908901526101008086015190890152610120808601519089015261014080860151908901526101608086015190890152610180808601511515908901526101a094850151151594909701939093525050509386019390860190600101613479565b509098975050505050505050565b6020808252825160608383015280516080840181905260009291820190839060a08601905b808310156135da5783516001600160a01b031682529284019260019290920191908401906135b1565b50838701516040870152604087015160608701528094505050505092915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561357e57888303603f19018552815180518452878101516001600160a01b038116898601526101a090508782015188860152606080830151828288015261366f83880182613346565b925050506080808301518683038288015261368a838261338a565b9250505060a080830151868303828801526136a5838261340a565b9250505060c080830151868303828801526136c0838261340a565b60e085810151908901526101008086015190890152610120808601519089015261014080860151908901526101608086015115159089015261018094850151151594909701939093525050509386019390860190600101613622565b602080825282518282018190526000919060409081850190868401855b82811015612fde5781518051855286810151151587860152858101511515868601526060908101516001600160601b03169085015260809093019290850190600101613739565b60c08101610fd18284612d84565b81516001600160a01b031681526020808301519082015260408101610fd1565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016137ec576137ec6137c4565b5060010190565b60006020828403121561380557600080fd5b5051919050565b60006020828403121561381e57600080fd5b81516103fa81612d16565b8051801515811461383957600080fd5b919050565b6000806040838503121561385157600080fd5b61385a83613829565b9150602083015190509250929050565b60006001600160401b0383111561388357613883613024565b613896601f8401601f1916602001613085565b90508281528383830111156138aa57600080fd5b6103fa8360208301846133ba565b600082601f8301126138c957600080fd5b6103fa8383516020850161386a565b6000602082840312156138ea57600080fd5b81516001600160401b0381111561390057600080fd5b61390c848285016138b8565b949350505050565b805161383981612d16565b805160ff8116811461383957600080fd5b60006020828403121561394257600080fd5b6103fa8261391f565b6001600160e01b031983168152815160009061396e8160048501602087016133ba565b919091016004019392505050565b6000825161398e8184602087016133ba565b9190910192915050565b80516001600160601b038116811461383957600080fd5b6000602082840312156139c157600080fd5b6103fa82613998565b6000606082840312156139dc57600080fd5b6139e461303a565b6139ed83613829565b81526139fb6020840161391f565b6020820152613a0c60408401613998565b60408201529392505050565b60006020808385031215613a2b57600080fd5b82516001600160401b03811115613a4157600080fd5b8301601f81018513613a5257600080fd5b8051613a606130f9826130b5565b81815260059190911b82018301908381019087831115613a7f57600080fd5b928401925b82841015611758578351613a9781612d16565b82529284019290840190613a84565b60008060008060808587031215613abc57600080fd5b505082516020840151604085015160609095015191969095509092509050565b8082028115828204841417610fd157610fd16137c4565b600060208284031215613b0557600080fd5b813563ffffffff811681146103fa57600080fd5b600082601f830112613b2a57600080fd5b81516020613b3a6130f9836130b5565b82815260059290921b84018101918181019086841115613b5957600080fd5b8286015b848110156131385780518352918301918301613b5d565b600082601f830112613b8557600080fd5b81516020613b956130f9836130b5565b82815260059290921b84018101918181019086841115613bb457600080fd5b8286015b848110156131385780516001600160401b03811115613bd75760008081fd5b613be58986838b01016138b8565b845250918301918301613bb8565b600082601f830112613c0457600080fd5b81516020613c146130f9836130b5565b82815260059290921b84018101918181019086841115613c3357600080fd5b8286015b848110156131385780516001600160401b03811115613c565760008081fd5b8701603f81018913613c685760008081fd5b613c7989868301516040840161386a565b845250918301918301613c37565b60008060008060808587031215613c9d57600080fd5b84516001600160401b0380821115613cb457600080fd5b818701915087601f830112613cc857600080fd5b81516020613cd86130f9836130b5565b82815260059290921b8401810191818101908b841115613cf757600080fd5b948201945b83861015613d1e578551613d0f81612d16565b82529482019490820190613cfc565b918a0151919850909350505080821115613d3757600080fd5b613d4388838901613b19565b94506040870151915080821115613d5957600080fd5b613d6588838901613b74565b93506060870151915080821115613d7b57600080fd5b50613d8887828801613bf3565b91505092959194509250565b600080600060608486031215613da957600080fd5b8351925060208401519150604084015190509250925092565b600060608284031215613dd457600080fd5b613ddc61303a565b613de583613829565b81526139fb60208401613829565b80820180821115610fd157610fd16137c4565b6020815260006103fa60208301846133de565b81810381811115610fd157610fd16137c4565b600082613e4957634e487b7160e01b600052601260045260246000fd5b500490565b60006101408284031215613e6157600080fd5b613e69613062565b82518152613e7960208401613914565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e0820152610100613ec8818501613829565b90820152610120613eda848201613829565b908201529392505050565b60008060008060008060008060006101208a8c031215613f0457600080fd5b8951985060208a0151613f1681612d16565b8098505060408a0151965060608a0151955060808a0151945060a08a0151935060c08a01519250613f4960e08b01613829565b9150613f586101008b01613829565b9050929598509295985092959856fea264697066735822122009fd0ac9e9875a214fc911c58e7b1131068a46d4bbccc9ed85416f4cd15102f864736f6c63430008130033
CErc20.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; interface CompLike { function delegate(address delegatee) external; } /** * @title Compound's CErc20 Contract * @notice CTokens which wrap an EIP-20 underlying * @author Compound */ contract CErc20 is CToken, CErc20Interface { /** * @notice Initialize the new money market * @param underlying_ The address of the underlying asset * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token */ function initialize(address underlying_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_) public { // CToken initialize does the bulk of the work super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); // Set underlying and sanity check it underlying = underlying_; EIP20Interface(underlying).totalSupply(); } /*** User Interface ***/ /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function mint(uint mintAmount) override external returns (uint) { mintInternal(mintAmount); return NO_ERROR; } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeem(uint redeemTokens) override external returns (uint) { redeemInternal(redeemTokens); return NO_ERROR; } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlying(uint redeemAmount) override external returns (uint) { redeemUnderlyingInternal(redeemAmount); return NO_ERROR; } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrow(uint borrowAmount) override external returns (uint) { borrowInternal(borrowAmount); return NO_ERROR; } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay, or -1 for the full outstanding amount * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrow(uint repayAmount) override external returns (uint) { repayBorrowInternal(repayAmount); return NO_ERROR; } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay, or -1 for the full outstanding amount * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrowBehalf(address borrower, uint repayAmount) override external returns (uint) { repayBorrowBehalfInternal(borrower, repayAmount); return NO_ERROR; } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param repayAmount The amount of the underlying borrowed asset to repay * @param cTokenCollateral The market in which to seize collateral from the borrower * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) override external returns (uint) { liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral); return NO_ERROR; } /** * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock) * @param token The address of the ERC-20 token to sweep */ function sweepToken(EIP20NonStandardInterface token) override external { require(msg.sender == admin, "CErc20::sweepToken: only admin can sweep tokens"); require(address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token"); uint256 balance = token.balanceOf(address(this)); token.transfer(admin, balance); } /** * @notice The sender adds to reserves. * @param addAmount The amount fo underlying token to add as reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReserves(uint addAmount) override external returns (uint) { return _addReservesInternal(addAmount); } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying tokens owned by this contract */ function getCashPrior() virtual override internal view returns (uint) { EIP20Interface token = EIP20Interface(underlying); return token.balanceOf(address(this)); } /** * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. * This will revert due to insufficient balance or insufficient allowance. * This function returns the actual amount received, * which may be less than `amount` if there is a fee attached to the transfer. * * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ function doTransferIn(address from, uint amount) virtual override internal returns (uint) { // Read from storage once address underlying_ = underlying; EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying_); uint balanceBefore = EIP20Interface(underlying_).balanceOf(address(this)); token.transferFrom(from, address(this), amount); bool success; assembly { switch returndatasize() case 0 { // This is a non-standard ERC-20 success := not(0) // set success to true } case 32 { // This is a compliant ERC-20 returndatacopy(0, 0, 32) success := mload(0) // Set `success = returndata` of override external call } default { // This is an excessively non-compliant ERC-20, revert. revert(0, 0) } } require(success, "TOKEN_TRANSFER_IN_FAILED"); // Calculate the amount that was *actually* transferred uint balanceAfter = EIP20Interface(underlying_).balanceOf(address(this)); return balanceAfter - balanceBefore; // underflow already checked above, just subtract } /** * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified * it is >= amount, this should not revert in normal conditions. * * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ function doTransferOut(address payable to, uint amount) virtual override internal { EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); token.transfer(to, amount); bool success; assembly { switch returndatasize() case 0 { // This is a non-standard ERC-20 success := not(0) // set success to true } case 32 { // This is a compliant ERC-20 returndatacopy(0, 0, 32) success := mload(0) // Set `success = returndata` of override external call } default { // This is an excessively non-compliant ERC-20, revert. revert(0, 0) } } require(success, "TOKEN_TRANSFER_OUT_FAILED"); } /** * @notice Admin call to delegate the votes of the COMP-like underlying * @param compLikeDelegatee The address to delegate votes to * @dev CTokens whose underlying are not CompLike should revert here */ function _delegateCompLikeTo(address compLikeDelegatee) external { require(msg.sender == admin, "only the admin may set the comp-like delegate"); CompLike(underlying).delegate(compLikeDelegatee); } }
CToken.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./ComptrollerInterface.sol"; import "./CTokenInterfaces.sol"; import "./ErrorReporter.sol"; import "./EIP20Interface.sol"; import "./InterestRateModel.sol"; import "./ExponentialNoError.sol"; /** * @title Compound's CToken Contract * @notice Abstract base for CTokens * @author Compound */ abstract contract CToken is CTokenInterface, ExponentialNoError, TokenErrorReporter { /** * @notice Initialize the money market * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ EIP-20 name of this token * @param symbol_ EIP-20 symbol of this token * @param decimals_ EIP-20 decimal precision of this token */ function initialize(ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_) public { require(msg.sender == admin, "only admin may initialize the market"); require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once"); // Set initial exchange rate initialExchangeRateMantissa = initialExchangeRateMantissa_; require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero."); // Set the comptroller uint err = _setComptroller(comptroller_); require(err == NO_ERROR, "setting comptroller failed"); // Initialize block number and borrow index (block number mocks depend on comptroller being set) accrualBlockNumber = getBlockNumber(); borrowIndex = mantissaOne; // Set the interest rate model (depends on block number / borrow index) err = _setInterestRateModelFresh(interestRateModel_); require(err == NO_ERROR, "setting interest rate model failed"); name = name_; symbol = symbol_; decimals = decimals_; // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund) _notEntered = true; } /** * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` * @dev Called by both `transfer` and `transferFrom` internally * @param spender The address of the account performing the transfer * @param src The address of the source account * @param dst The address of the destination account * @param tokens The number of tokens to transfer * @return 0 if the transfer succeeded, else revert */ function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { /* Fail if transfer not allowed */ uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens); if (allowed != 0) { revert TransferComptrollerRejection(allowed); } /* Do not allow self-transfers */ if (src == dst) { revert TransferNotAllowed(); } /* Get the allowance, infinite for the account owner */ uint startingAllowance = 0; if (spender == src) { startingAllowance = type(uint).max; } else { startingAllowance = transferAllowances[src][spender]; } /* Do the calculations, checking for {under,over}flow */ uint allowanceNew = startingAllowance - tokens; uint srcTokensNew = accountTokens[src] - tokens; uint dstTokensNew = accountTokens[dst] + tokens; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) accountTokens[src] = srcTokensNew; accountTokens[dst] = dstTokensNew; /* Eat some of the allowance (if necessary) */ if (startingAllowance != type(uint).max) { transferAllowances[src][spender] = allowanceNew; } /* We emit a Transfer event */ emit Transfer(src, dst, tokens); // unused function // comptroller.transferVerify(address(this), src, dst, tokens); return NO_ERROR; } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) override external nonReentrant returns (bool) { return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR; } /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 amount) override external nonReentrant returns (bool) { return transferTokens(msg.sender, src, dst, amount) == NO_ERROR; } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (uint256.max means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) override external returns (bool) { address src = msg.sender; transferAllowances[src][spender] = amount; emit Approval(src, spender, amount); return true; } /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) override external view returns (uint256) { return transferAllowances[owner][spender]; } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) override external view returns (uint256) { return accountTokens[owner]; } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) override external returns (uint) { Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()}); return mul_ScalarTruncate(exchangeRate, accountTokens[owner]); } /** * @notice Get a snapshot of the account's balances, and the cached exchange rate * @dev This is used by comptroller to more efficiently perform liquidity checks. * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) override external view returns (uint, uint, uint, uint) { return ( NO_ERROR, accountTokens[account], borrowBalanceStoredInternal(account), exchangeRateStoredInternal() ); } /** * @dev Function to simply retrieve block number * This exists mainly for inheriting test contracts to stub this result. */ function getBlockNumber() virtual internal view returns (uint) { return block.number; } /** * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ function borrowRatePerBlock() override external view returns (uint) { return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); } /** * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ function supplyRatePerBlock() override external view returns (uint) { return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa); } /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ function totalBorrowsCurrent() override external nonReentrant returns (uint) { accrueInterest(); return totalBorrows; } /** * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ function borrowBalanceCurrent(address account) override external nonReentrant returns (uint) { accrueInterest(); return borrowBalanceStored(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) override public view returns (uint) { return borrowBalanceStoredInternal(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return (error code, the calculated balance or 0 if error code is non-zero) */ function borrowBalanceStoredInternal(address account) internal view returns (uint) { /* Get borrowBalance and borrowIndex */ BorrowSnapshot storage borrowSnapshot = accountBorrows[account]; /* If borrowBalance = 0 then borrowIndex is likely also 0. * Rather than failing the calculation with a division by 0, we immediately return 0 in this case. */ if (borrowSnapshot.principal == 0) { return 0; } /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ uint principalTimesIndex = borrowSnapshot.principal * borrowIndex; return principalTimesIndex / borrowSnapshot.interestIndex; } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() override public nonReentrant returns (uint) { accrueInterest(); return exchangeRateStored(); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateStored() override public view returns (uint) { return exchangeRateStoredInternal(); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return calculated exchange rate scaled by 1e18 */ function exchangeRateStoredInternal() virtual internal view returns (uint) { uint _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return initialExchangeRateMantissa; } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint totalCash = getCashPrior(); uint cashPlusBorrowsMinusReserves = totalCash + totalBorrows - totalReserves; uint exchangeRate = cashPlusBorrowsMinusReserves * expScale / _totalSupply; return exchangeRate; } } /** * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() override external view returns (uint) { return getCashPrior(); } /** * @notice Applies accrued interest to total borrows and reserves * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ function accrueInterest() virtual override public returns (uint) { /* Remember the initial block number */ uint currentBlockNumber = getBlockNumber(); uint accrualBlockNumberPrior = accrualBlockNumber; /* Short-circuit accumulating 0 interest */ if (accrualBlockNumberPrior == currentBlockNumber) { return NO_ERROR; } /* Read the previous values out of storage */ uint cashPrior = getCashPrior(); uint borrowsPrior = totalBorrows; uint reservesPrior = totalReserves; uint borrowIndexPrior = borrowIndex; /* Calculate the current borrow interest rate */ uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); /* Calculate the number of blocks elapsed since the last accrual */ uint blockDelta = currentBlockNumber - accrualBlockNumberPrior; /* * Calculate the interest accumulated into borrows and reserves and the new index: * simpleInterestFactor = borrowRate * blockDelta * interestAccumulated = simpleInterestFactor * totalBorrows * totalBorrowsNew = interestAccumulated + totalBorrows * totalReservesNew = interestAccumulated * reserveFactor + totalReserves * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex */ Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), blockDelta); uint interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior); uint totalBorrowsNew = interestAccumulated + borrowsPrior; uint totalReservesNew = mul_ScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior); uint borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accrualBlockNumber = currentBlockNumber; borrowIndex = borrowIndexNew; totalBorrows = totalBorrowsNew; totalReserves = totalReservesNew; /* We emit an AccrueInterest event */ emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); return NO_ERROR; } /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply */ function mintInternal(uint mintAmount) internal nonReentrant { accrueInterest(); // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to mintFresh(msg.sender, mintAmount); } /** * @notice User supplies assets into the market and receives cTokens in exchange * @dev Assumes interest has already been accrued up to the current block * @param minter The address of the account which is supplying the assets * @param mintAmount The amount of the underlying asset to supply */ function mintFresh(address minter, uint mintAmount) internal { /* Fail if mint not allowed */ uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount); if (allowed != 0) { revert MintComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert MintFreshnessCheck(); } Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call `doTransferIn` for the minter and the mintAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * `doTransferIn` reverts if anything goes wrong, since we can't be sure if * side-effects occurred. The function returns the amount actually transferred, * in case of a fee. On success, the cToken holds an additional `actualMintAmount` * of cash. */ uint actualMintAmount = doTransferIn(minter, mintAmount); /* * We get the current exchange rate and calculate the number of cTokens to be minted: * mintTokens = actualMintAmount / exchangeRate */ uint mintTokens = div_(actualMintAmount, exchangeRate); /* * We calculate the new total supply of cTokens and minter token balance, checking for overflow: * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[minter] + mintTokens * And write them into storage */ totalSupply = totalSupply + mintTokens; accountTokens[minter] = accountTokens[minter] + mintTokens; /* We emit a Mint event, and a Transfer event */ emit Mint(minter, actualMintAmount, mintTokens); emit Transfer(address(this), minter, mintTokens); /* We call the defense hook */ comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying */ function redeemInternal(uint redeemTokens) internal nonReentrant { accrueInterest(); // redeemFresh emits redeem-specific logs on errors, so we don't need to redeemFresh(payable(msg.sender), redeemTokens, 0); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to receive from redeeming cTokens */ function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant { accrueInterest(); // redeemFresh emits redeem-specific logs on errors, so we don't need to redeemFresh(payable(msg.sender), 0, redeemAmount); } /** * @notice User redeems cTokens in exchange for the underlying asset * @dev Assumes interest has already been accrued up to the current block * @param redeemer The address of the account which is redeeming the tokens * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) */ function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal { require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); /* exchangeRate = invoke Exchange Rate Stored() */ Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal() }); uint redeemTokens; uint redeemAmount; /* If redeemTokensIn > 0: */ if (redeemTokensIn > 0) { /* * We calculate the exchange rate and the amount of underlying to be redeemed: * redeemTokens = redeemTokensIn * redeemAmount = redeemTokensIn x exchangeRateCurrent */ redeemTokens = redeemTokensIn; redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn); } else { /* * We get the current exchange rate and calculate the amount to be redeemed: * redeemTokens = redeemAmountIn / exchangeRate * redeemAmount = redeemAmountIn */ redeemTokens = div_(redeemAmountIn, exchangeRate); redeemAmount = redeemAmountIn; } /* Fail if redeem not allowed */ uint allowed = comptroller.redeemAllowed(address(this), redeemer, redeemTokens); if (allowed != 0) { revert RedeemComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RedeemFreshnessCheck(); } /* Fail gracefully if protocol has insufficient cash */ if (getCashPrior() < redeemAmount) { revert RedeemTransferOutNotPossible(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We write the previously calculated values into storage. * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer. */ totalSupply = totalSupply - redeemTokens; accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens; /* * We invoke doTransferOut for the redeemer and the redeemAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken has redeemAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(redeemer, redeemAmount); /* We emit a Transfer event, and a Redeem event */ emit Transfer(redeemer, address(this), redeemTokens); emit Redeem(redeemer, redeemAmount, redeemTokens); /* We call the defense hook */ comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowInternal(uint borrowAmount) internal nonReentrant { accrueInterest(); // borrowFresh emits borrow-specific logs on errors, so we don't need to borrowFresh(payable(msg.sender), borrowAmount); } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowFresh(address payable borrower, uint borrowAmount) internal { /* Fail if borrow not allowed */ uint allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); if (allowed != 0) { revert BorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert BorrowFreshnessCheck(); } /* Fail gracefully if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { revert BorrowCashNotAvailable(); } /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowNew = accountBorrow + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower); uint accountBorrowsNew = accountBorrowsPrev + borrowAmount; uint totalBorrowsNew = totalBorrows + borrowAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We write the previously calculated values into storage. * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer. `*/ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* * We invoke doTransferOut for the borrower and the borrowAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(borrower, borrowAmount); /* We emit a Borrow event */ emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay, or -1 for the full outstanding amount */ function repayBorrowInternal(uint repayAmount) internal nonReentrant { accrueInterest(); // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to repayBorrowFresh(msg.sender, msg.sender, repayAmount); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay, or -1 for the full outstanding amount */ function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant { accrueInterest(); // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to repayBorrowFresh(msg.sender, borrower, repayAmount); } /** * @notice Borrows are repaid by another user (possibly the borrower). * @param payer the account paying off the borrow * @param borrower the account with the debt being payed off * @param repayAmount the amount of underlying tokens being returned, or -1 for the full outstanding amount * @return (uint) the actual repayment amount. */ function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal returns (uint) { /* Fail if repayBorrow not allowed */ uint allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); if (allowed != 0) { revert RepayBorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RepayBorrowFreshnessCheck(); } /* We fetch the amount the borrower owes, with accumulated interest */ uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower); /* If repayAmount == -1, repayAmount = accountBorrows */ uint repayAmountFinal = repayAmount == type(uint).max ? accountBorrowsPrev : repayAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the payer and the repayAmount * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken holds an additional repayAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ uint actualRepayAmount = doTransferIn(payer, repayAmountFinal); /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount; uint totalBorrowsNew = totalBorrows - actualRepayAmount; /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* We emit a RepayBorrow event */ emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew); return actualRepayAmount; } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay */ function liquidateBorrowInternal(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal nonReentrant { accrueInterest(); uint error = cTokenCollateral.accrueInterest(); if (error != NO_ERROR) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed revert LiquidateAccrueCollateralInterestFailed(error); } // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral); } /** * @notice The liquidator liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param liquidator The address repaying the borrow and seizing collateral * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay */ function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal { /* Fail if liquidate not allowed */ uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(cTokenCollateral), liquidator, borrower, repayAmount); if (allowed != 0) { revert LiquidateComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert LiquidateFreshnessCheck(); } /* Verify cTokenCollateral market's block number equals current block number */ if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) { revert LiquidateCollateralFreshnessCheck(); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateLiquidatorIsBorrower(); } /* Fail if repayAmount = 0 */ if (repayAmount == 0) { revert LiquidateCloseAmountIsZero(); } /* Fail if repayAmount = -1 */ if (repayAmount == type(uint).max) { revert LiquidateCloseAmountIsUintMax(); } /* Fail if repayBorrow fails */ uint actualRepayAmount = repayBorrowFresh(liquidator, borrower, repayAmount); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We calculate the number of collateral tokens that will be seized */ (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateCalculateSeizeTokens(address(this), address(cTokenCollateral), actualRepayAmount); require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"); /* Revert if borrower collateral token balance < seizeTokens */ require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH"); // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call if (address(cTokenCollateral) == address(this)) { seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { require(cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed"); } /* We emit a LiquidateBorrow event */ emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another cToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seize(address liquidator, address borrower, uint seizeTokens) override external nonReentrant returns (uint) { seizeInternal(msg.sender, liquidator, borrower, seizeTokens); return NO_ERROR; } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize */ function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal { /* Fail if seize not allowed */ uint allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); if (allowed != 0) { revert LiquidateSeizeComptrollerRejection(allowed); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateSeizeLiquidatorIsBorrower(); } /* * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: * borrowerTokensNew = accountTokens[borrower] - seizeTokens * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens */ uint protocolSeizeTokens = mul_(seizeTokens, Exp({mantissa: protocolSeizeShareMantissa})); uint liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens; Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); uint protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens); uint totalReservesNew = totalReserves + protocolSeizeAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the calculated values into storage */ totalReserves = totalReservesNew; totalSupply = totalSupply - protocolSeizeTokens; accountTokens[borrower] = accountTokens[borrower] - seizeTokens; accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens; /* Emit a Transfer event */ emit Transfer(borrower, liquidator, liquidatorSeizeTokens); emit Transfer(borrower, address(this), protocolSeizeTokens); emit ReservesAdded(address(this), protocolSeizeAmount, totalReservesNew); } /*** Admin Functions ***/ /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address payable newPendingAdmin) override external returns (uint) { // Check caller = admin if (msg.sender != admin) { revert SetPendingAdminOwnerCheck(); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return NO_ERROR; } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() override external returns (uint) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin || msg.sender == address(0)) { revert AcceptAdminPendingAdminCheck(); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = payable(address(0)); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return NO_ERROR; } /** * @notice Sets a new comptroller for the market * @dev Admin function to set a new comptroller * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setComptroller(ComptrollerInterface newComptroller) override public returns (uint) { // Check caller is admin if (msg.sender != admin) { revert SetComptrollerOwnerCheck(); } ComptrollerInterface oldComptroller = comptroller; // Ensure invoke comptroller.isComptroller() returns true require(newComptroller.isComptroller(), "marker method returned false"); // Set market's comptroller to newComptroller comptroller = newComptroller; // Emit NewComptroller(oldComptroller, newComptroller) emit NewComptroller(oldComptroller, newComptroller); return NO_ERROR; } /** * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh * @dev Admin function to accrue interest and set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactor(uint newReserveFactorMantissa) override external nonReentrant returns (uint) { accrueInterest(); // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to. return _setReserveFactorFresh(newReserveFactorMantissa); } /** * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) * @dev Admin function to set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) { // Check caller is admin if (msg.sender != admin) { revert SetReserveFactorAdminCheck(); } // Verify market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert SetReserveFactorFreshCheck(); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > reserveFactorMaxMantissa) { revert SetReserveFactorBoundsCheck(); } uint oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); return NO_ERROR; } /** * @notice Accrues interest and reduces reserves by transferring from msg.sender * @param addAmount Amount of addition to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) { accrueInterest(); // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. _addReservesFresh(addAmount); return NO_ERROR; } /** * @notice Add reserves by transferring from caller * @dev Requires fresh interest accrual * @param addAmount Amount of addition to reserves * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees */ function _addReservesFresh(uint addAmount) internal returns (uint, uint) { // totalReserves + actualAddAmount uint totalReservesNew; uint actualAddAmount; // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert AddReservesFactorFreshCheck(actualAddAmount); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the caller and the addAmount * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken holds an additional addAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ actualAddAmount = doTransferIn(msg.sender, addAmount); totalReservesNew = totalReserves + actualAddAmount; // Store reserves[n+1] = reserves[n] + actualAddAmount totalReserves = totalReservesNew; /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */ emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew); /* Return (NO_ERROR, actualAddAmount) */ return (NO_ERROR, actualAddAmount); } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReserves(uint reduceAmount) override external nonReentrant returns (uint) { accrueInterest(); // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to. return _reduceReservesFresh(reduceAmount); } /** * @notice Reduces reserves by transferring to admin * @dev Requires fresh interest accrual * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReservesFresh(uint reduceAmount) internal returns (uint) { // totalReserves - reduceAmount uint totalReservesNew; // Check caller is admin if (msg.sender != admin) { revert ReduceReservesAdminCheck(); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert ReduceReservesFreshCheck(); } // Fail gracefully if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { revert ReduceReservesCashNotAvailable(); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { revert ReduceReservesCashValidation(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) totalReservesNew = totalReserves - reduceAmount; // Store reserves[n+1] = reserves[n] - reduceAmount totalReserves = totalReservesNew; // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. doTransferOut(admin, reduceAmount); emit ReservesReduced(admin, reduceAmount, totalReservesNew); return NO_ERROR; } /** * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh * @dev Admin function to accrue interest and update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModel(InterestRateModel newInterestRateModel) override public returns (uint) { accrueInterest(); // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to. return _setInterestRateModelFresh(newInterestRateModel); } /** * @notice updates the interest rate model (*requires fresh interest accrual) * @dev Admin function to update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; // Check caller is admin if (msg.sender != admin) { revert SetInterestRateModelOwnerCheck(); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert SetInterestRateModelFreshCheck(); } // Track the market's current interest rate model oldInterestRateModel = interestRateModel; // Ensure invoke newInterestRateModel.isInterestRateModel() returns true require(newInterestRateModel.isInterestRateModel(), "marker method returned false"); // Set the interest rate model to newInterestRateModel interestRateModel = newInterestRateModel; // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); return NO_ERROR; } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying owned by this contract */ function getCashPrior() virtual internal view returns (uint); /** * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. * This may revert due to insufficient balance or insufficient allowance. */ function doTransferIn(address from, uint amount) virtual internal returns (uint); /** * @dev Performs a transfer out, ideally returning an explanatory error code upon failure rather than reverting. * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. */ function doTransferOut(address payable to, uint amount) virtual internal; /*** Reentrancy Guard ***/ /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() { require(_notEntered, "re-entered"); _notEntered = false; _; _notEntered = true; // get a gas-refund post-Istanbul } }
PriceOracle.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; abstract contract PriceOracle { /// @notice Indicator that this is a PriceOracle contract (for inspection) bool public constant isPriceOracle = true; /** * @notice Get the underlying price of a cToken asset * @param cToken The cToken to get the underlying price of * @return The underlying asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getUnderlyingPrice(CToken cToken) virtual external view returns (uint); }
ErrorReporter.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; contract ComptrollerErrorReporter { enum Error { NO_ERROR, UNAUTHORIZED, COMPTROLLER_MISMATCH, INSUFFICIENT_SHORTFALL, INSUFFICIENT_LIQUIDITY, INVALID_CLOSE_FACTOR, INVALID_COLLATERAL_FACTOR, INVALID_LIQUIDATION_INCENTIVE, MARKET_NOT_ENTERED, // no longer possible MARKET_NOT_LISTED, MARKET_ALREADY_LISTED, MATH_ERROR, NONZERO_BORROW_BALANCE, PRICE_ERROR, REJECTION, SNAPSHOT_ERROR, TOO_MANY_ASSETS, TOO_MUCH_REPAY } enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, EXIT_MARKET_BALANCE_OWED, EXIT_MARKET_REJECTION, SET_CLOSE_FACTOR_OWNER_CHECK, SET_CLOSE_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_NO_EXISTS, SET_COLLATERAL_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_WITHOUT_PRICE, SET_IMPLEMENTATION_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_VALIDATION, SET_MAX_ASSETS_OWNER_CHECK, SET_PENDING_ADMIN_OWNER_CHECK, SET_PENDING_IMPLEMENTATION_OWNER_CHECK, SET_PRICE_ORACLE_OWNER_CHECK, SUPPORT_MARKET_EXISTS, SUPPORT_MARKET_OWNER_CHECK, SET_PAUSE_GUARDIAN_OWNER_CHECK } /** * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary * contract-specific code that enables us to report opaque error codes from upgradeable contracts. **/ event Failure(uint error, uint info, uint detail); /** * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator */ function fail(Error err, FailureInfo info) internal returns (uint) { emit Failure(uint(err), uint(info), 0); return uint(err); } /** * @dev use this when reporting an opaque error from an upgradeable collaborator contract */ function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { emit Failure(uint(err), uint(info), opaqueError); return uint(err); } } contract TokenErrorReporter { uint public constant NO_ERROR = 0; // support legacy return codes error TransferComptrollerRejection(uint256 errorCode); error TransferNotAllowed(); error TransferNotEnough(); error TransferTooMuch(); error MintComptrollerRejection(uint256 errorCode); error MintFreshnessCheck(); error RedeemComptrollerRejection(uint256 errorCode); error RedeemFreshnessCheck(); error RedeemTransferOutNotPossible(); error BorrowComptrollerRejection(uint256 errorCode); error BorrowFreshnessCheck(); error BorrowCashNotAvailable(); error RepayBorrowComptrollerRejection(uint256 errorCode); error RepayBorrowFreshnessCheck(); error LiquidateComptrollerRejection(uint256 errorCode); error LiquidateFreshnessCheck(); error LiquidateCollateralFreshnessCheck(); error LiquidateAccrueBorrowInterestFailed(uint256 errorCode); error LiquidateAccrueCollateralInterestFailed(uint256 errorCode); error LiquidateLiquidatorIsBorrower(); error LiquidateCloseAmountIsZero(); error LiquidateCloseAmountIsUintMax(); error LiquidateRepayBorrowFreshFailed(uint256 errorCode); error LiquidateSeizeComptrollerRejection(uint256 errorCode); error LiquidateSeizeLiquidatorIsBorrower(); error AcceptAdminPendingAdminCheck(); error SetComptrollerOwnerCheck(); error SetPendingAdminOwnerCheck(); error SetReserveFactorAdminCheck(); error SetReserveFactorFreshCheck(); error SetReserveFactorBoundsCheck(); error AddReservesFactorFreshCheck(uint256 actualAddAmount); error ReduceReservesAdminCheck(); error ReduceReservesFreshCheck(); error ReduceReservesCashNotAvailable(); error ReduceReservesCashValidation(); error SetInterestRateModelOwnerCheck(); error SetInterestRateModelFreshCheck(); }
MoaiLens.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "../CErc20.sol"; import "../CToken.sol"; import "../PriceOracle.sol"; import "../EIP20Interface.sol"; import "../Governance/GovernorAlpha.sol"; import "../Governance/Comp.sol"; interface ComptrollerLensInterface { function markets(address) external view returns (bool, uint); function oracle() external view returns (PriceOracle); function getAccountLiquidity(address) external view returns (uint, uint, uint); function getAssetsIn(address) external view returns (CToken[] memory); function claimComp(address) external; function compAccrued(address) external view returns (uint); function compSpeeds(address) external view returns (uint); function compSupplySpeeds(address) external view returns (uint); function compBorrowSpeeds(address) external view returns (uint); function borrowCaps(address) external view returns (uint); } interface GovernorBravoInterface { struct Receipt { bool hasVoted; uint8 support; uint96 votes; } struct Proposal { uint id; address proposer; uint eta; uint startBlock; uint endBlock; uint forVotes; uint againstVotes; uint abstainVotes; bool canceled; bool executed; } function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas); function proposals(uint proposalId) external view returns (Proposal memory); function getReceipt(uint proposalId, address voter) external view returns (Receipt memory); } contract MoaiLens { struct CTokenMetadata { address cToken; uint exchangeRateCurrent; uint supplyRatePerBlock; uint borrowRatePerBlock; uint reserveFactorMantissa; uint totalBorrows; uint totalReserves; uint totalSupply; uint totalCash; bool isListed; uint collateralFactorMantissa; address underlyingAssetAddress; uint cTokenDecimals; uint underlyingDecimals; uint compSupplySpeed; uint compBorrowSpeed; uint borrowCap; } function getCompSpeeds(ComptrollerLensInterface comptroller, CToken cToken) internal returns (uint, uint) { // Getting comp speeds is gnarly due to not every network having the // split comp speeds from Proposal 62 and other networks don't even // have comp speeds. uint compSupplySpeed = 0; (bool compSupplySpeedSuccess, bytes memory compSupplySpeedReturnData) = address(comptroller).call( abi.encodePacked( comptroller.compSupplySpeeds.selector, abi.encode(address(cToken)) ) ); if (compSupplySpeedSuccess) { compSupplySpeed = abi.decode(compSupplySpeedReturnData, (uint)); } uint compBorrowSpeed = 0; (bool compBorrowSpeedSuccess, bytes memory compBorrowSpeedReturnData) = address(comptroller).call( abi.encodePacked( comptroller.compBorrowSpeeds.selector, abi.encode(address(cToken)) ) ); if (compBorrowSpeedSuccess) { compBorrowSpeed = abi.decode(compBorrowSpeedReturnData, (uint)); } // If the split comp speeds call doesn't work, try the oldest non-spit version. if (!compSupplySpeedSuccess || !compBorrowSpeedSuccess) { (bool compSpeedSuccess, bytes memory compSpeedReturnData) = address(comptroller).call( abi.encodePacked( comptroller.compSpeeds.selector, abi.encode(address(cToken)) ) ); if (compSpeedSuccess) { compSupplySpeed = compBorrowSpeed = abi.decode(compSpeedReturnData, (uint)); } } return (compSupplySpeed, compBorrowSpeed); } function cTokenMetadata(CToken cToken) public returns (CTokenMetadata memory) { uint exchangeRateCurrent = cToken.exchangeRateCurrent(); ComptrollerLensInterface comptroller = ComptrollerLensInterface(address(cToken.comptroller())); (bool isListed, uint collateralFactorMantissa) = comptroller.markets(address(cToken)); address underlyingAssetAddress; uint underlyingDecimals; if (compareStrings(cToken.symbol(), "cETH")) { underlyingAssetAddress = address(0); underlyingDecimals = 18; } else { CErc20 cErc20 = CErc20(address(cToken)); underlyingAssetAddress = cErc20.underlying(); underlyingDecimals = EIP20Interface(cErc20.underlying()).decimals(); } (uint compSupplySpeed, uint compBorrowSpeed) = getCompSpeeds(comptroller, cToken); uint borrowCap = 0; (bool borrowCapSuccess, bytes memory borrowCapReturnData) = address(comptroller).call( abi.encodePacked( comptroller.borrowCaps.selector, abi.encode(address(cToken)) ) ); if (borrowCapSuccess) { borrowCap = abi.decode(borrowCapReturnData, (uint)); } return CTokenMetadata({ cToken: address(cToken), exchangeRateCurrent: exchangeRateCurrent, supplyRatePerBlock: cToken.supplyRatePerBlock(), borrowRatePerBlock: cToken.borrowRatePerBlock(), reserveFactorMantissa: cToken.reserveFactorMantissa(), totalBorrows: cToken.totalBorrows(), totalReserves: cToken.totalReserves(), totalSupply: cToken.totalSupply(), totalCash: cToken.getCash(), isListed: isListed, collateralFactorMantissa: collateralFactorMantissa, underlyingAssetAddress: underlyingAssetAddress, cTokenDecimals: cToken.decimals(), underlyingDecimals: underlyingDecimals, compSupplySpeed: compSupplySpeed, compBorrowSpeed: compBorrowSpeed, borrowCap: borrowCap }); } function cTokenMetadataAll(CToken[] calldata cTokens) external returns (CTokenMetadata[] memory) { uint cTokenCount = cTokens.length; CTokenMetadata[] memory res = new CTokenMetadata[](cTokenCount); for (uint i = 0; i < cTokenCount; i++) { res[i] = cTokenMetadata(cTokens[i]); } return res; } struct CTokenBalances { address cToken; uint balanceOf; uint borrowBalanceCurrent; uint balanceOfUnderlying; uint tokenBalance; uint tokenAllowance; } function cTokenBalances(CToken cToken, address payable account) public returns (CTokenBalances memory) { uint balanceOf = cToken.balanceOf(account); uint borrowBalanceCurrent = cToken.borrowBalanceCurrent(account); uint balanceOfUnderlying = cToken.balanceOfUnderlying(account); uint tokenBalance; uint tokenAllowance; if (compareStrings(cToken.symbol(), "cETH")) { tokenBalance = account.balance; tokenAllowance = account.balance; } else { CErc20 cErc20 = CErc20(address(cToken)); EIP20Interface underlying = EIP20Interface(cErc20.underlying()); tokenBalance = underlying.balanceOf(account); tokenAllowance = underlying.allowance(account, address(cToken)); } return CTokenBalances({ cToken: address(cToken), balanceOf: balanceOf, borrowBalanceCurrent: borrowBalanceCurrent, balanceOfUnderlying: balanceOfUnderlying, tokenBalance: tokenBalance, tokenAllowance: tokenAllowance }); } function cTokenBalancesAll(CToken[] calldata cTokens, address payable account) external returns (CTokenBalances[] memory) { uint cTokenCount = cTokens.length; CTokenBalances[] memory res = new CTokenBalances[](cTokenCount); for (uint i = 0; i < cTokenCount; i++) { res[i] = cTokenBalances(cTokens[i], account); } return res; } struct CTokenUnderlyingPrice { address cToken; uint underlyingPrice; } function cTokenUnderlyingPrice(CToken cToken) public returns (CTokenUnderlyingPrice memory) { ComptrollerLensInterface comptroller = ComptrollerLensInterface(address(cToken.comptroller())); PriceOracle priceOracle = comptroller.oracle(); return CTokenUnderlyingPrice({ cToken: address(cToken), underlyingPrice: priceOracle.getUnderlyingPrice(cToken) }); } function cTokenUnderlyingPriceAll(CToken[] calldata cTokens) external returns (CTokenUnderlyingPrice[] memory) { uint cTokenCount = cTokens.length; CTokenUnderlyingPrice[] memory res = new CTokenUnderlyingPrice[](cTokenCount); for (uint i = 0; i < cTokenCount; i++) { res[i] = cTokenUnderlyingPrice(cTokens[i]); } return res; } struct AccountLimits { CToken[] markets; uint liquidity; uint shortfall; } function getAccountLimits(ComptrollerLensInterface comptroller, address account) public returns (AccountLimits memory) { (uint errorCode, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(account); require(errorCode == 0); return AccountLimits({ markets: comptroller.getAssetsIn(account), liquidity: liquidity, shortfall: shortfall }); } struct GovReceipt { uint proposalId; bool hasVoted; bool support; uint96 votes; } function getGovReceipts(GovernorAlpha governor, address voter, uint[] memory proposalIds) public view returns (GovReceipt[] memory) { uint proposalCount = proposalIds.length; GovReceipt[] memory res = new GovReceipt[](proposalCount); for (uint i = 0; i < proposalCount; i++) { GovernorAlpha.Receipt memory receipt = governor.getReceipt(proposalIds[i], voter); res[i] = GovReceipt({ proposalId: proposalIds[i], hasVoted: receipt.hasVoted, support: receipt.support, votes: receipt.votes }); } return res; } struct GovBravoReceipt { uint proposalId; bool hasVoted; uint8 support; uint96 votes; } function getGovBravoReceipts(GovernorBravoInterface governor, address voter, uint[] memory proposalIds) public view returns (GovBravoReceipt[] memory) { uint proposalCount = proposalIds.length; GovBravoReceipt[] memory res = new GovBravoReceipt[](proposalCount); for (uint i = 0; i < proposalCount; i++) { GovernorBravoInterface.Receipt memory receipt = governor.getReceipt(proposalIds[i], voter); res[i] = GovBravoReceipt({ proposalId: proposalIds[i], hasVoted: receipt.hasVoted, support: receipt.support, votes: receipt.votes }); } return res; } struct GovProposal { uint proposalId; address proposer; uint eta; address[] targets; uint[] values; string[] signatures; bytes[] calldatas; uint startBlock; uint endBlock; uint forVotes; uint againstVotes; bool canceled; bool executed; } function setProposal(GovProposal memory res, GovernorAlpha governor, uint proposalId) internal view { ( , address proposer, uint eta, uint startBlock, uint endBlock, uint forVotes, uint againstVotes, bool canceled, bool executed ) = governor.proposals(proposalId); res.proposalId = proposalId; res.proposer = proposer; res.eta = eta; res.startBlock = startBlock; res.endBlock = endBlock; res.forVotes = forVotes; res.againstVotes = againstVotes; res.canceled = canceled; res.executed = executed; } function getGovProposals(GovernorAlpha governor, uint[] calldata proposalIds) external view returns (GovProposal[] memory) { GovProposal[] memory res = new GovProposal[](proposalIds.length); for (uint i = 0; i < proposalIds.length; i++) { ( address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas ) = governor.getActions(proposalIds[i]); res[i] = GovProposal({ proposalId: 0, proposer: address(0), eta: 0, targets: targets, values: values, signatures: signatures, calldatas: calldatas, startBlock: 0, endBlock: 0, forVotes: 0, againstVotes: 0, canceled: false, executed: false }); setProposal(res[i], governor, proposalIds[i]); } return res; } struct GovBravoProposal { uint proposalId; address proposer; uint eta; address[] targets; uint[] values; string[] signatures; bytes[] calldatas; uint startBlock; uint endBlock; uint forVotes; uint againstVotes; uint abstainVotes; bool canceled; bool executed; } function setBravoProposal(GovBravoProposal memory res, GovernorBravoInterface governor, uint proposalId) internal view { GovernorBravoInterface.Proposal memory p = governor.proposals(proposalId); res.proposalId = proposalId; res.proposer = p.proposer; res.eta = p.eta; res.startBlock = p.startBlock; res.endBlock = p.endBlock; res.forVotes = p.forVotes; res.againstVotes = p.againstVotes; res.abstainVotes = p.abstainVotes; res.canceled = p.canceled; res.executed = p.executed; } function getGovBravoProposals(GovernorBravoInterface governor, uint[] calldata proposalIds) external view returns (GovBravoProposal[] memory) { GovBravoProposal[] memory res = new GovBravoProposal[](proposalIds.length); for (uint i = 0; i < proposalIds.length; i++) { ( address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas ) = governor.getActions(proposalIds[i]); res[i] = GovBravoProposal({ proposalId: 0, proposer: address(0), eta: 0, targets: targets, values: values, signatures: signatures, calldatas: calldatas, startBlock: 0, endBlock: 0, forVotes: 0, againstVotes: 0, abstainVotes: 0, canceled: false, executed: false }); setBravoProposal(res[i], governor, proposalIds[i]); } return res; } struct CompBalanceMetadata { uint balance; uint votes; address delegate; } function getCompBalanceMetadata(Comp comp, address account) external view returns (CompBalanceMetadata memory) { return CompBalanceMetadata({ balance: comp.balanceOf(account), votes: uint256(comp.getCurrentVotes(account)), delegate: comp.delegates(account) }); } struct CompBalanceMetadataExt { uint balance; uint votes; address delegate; uint allocated; } function getCompBalanceMetadataExt(Comp comp, ComptrollerLensInterface comptroller, address account) external returns (CompBalanceMetadataExt memory) { uint balance = comp.balanceOf(account); comptroller.claimComp(account); uint newBalance = comp.balanceOf(account); uint accrued = comptroller.compAccrued(account); uint total = add(accrued, newBalance, "sum comp total"); uint allocated = sub(total, balance, "sub allocated"); return CompBalanceMetadataExt({ balance: balance, votes: uint256(comp.getCurrentVotes(account)), delegate: comp.delegates(account), allocated: allocated }); } struct CompVotes { uint blockNumber; uint votes; } function getCompVotes(Comp comp, address account, uint32[] calldata blockNumbers) external view returns (CompVotes[] memory) { CompVotes[] memory res = new CompVotes[](blockNumbers.length); for (uint i = 0; i < blockNumbers.length; i++) { res[i] = CompVotes({ blockNumber: uint256(blockNumbers[i]), votes: uint256(comp.getPriorVotes(account, blockNumbers[i])) }); } return res; } function compareStrings(string memory a, string memory b) internal pure returns (bool) { return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { uint c = a + b; require(c >= a, errorMessage); return c; } function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { require(b <= a, errorMessage); uint c = a - b; return c; } }
EIP20Interface.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title ERC 20 Token Standard Interface * https://eips.ethereum.org/EIPS/eip-20 */ interface EIP20Interface { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return balance The balance */ function balanceOf(address owner) external view returns (uint256 balance); /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return success Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external returns (bool success); /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer * @return success Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 amount) external returns (bool success); /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved (-1 means infinite) * @return success Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return remaining The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); }
Comp.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; contract Comp { /// @notice EIP-20 token name for this token string public constant name = "Compound"; /// @notice EIP-20 token symbol for this token string public constant symbol = "COMP"; /// @notice EIP-20 token decimals for this token uint8 public constant decimals = 18; /// @notice Total number of tokens in circulation uint public constant totalSupply = 10000000e18; // 10 million Comp /// @notice Allowance amounts on behalf of others mapping (address => mapping (address => uint96)) internal allowances; /// @notice Official record of token balances for each account mapping (address => uint96) internal balances; /// @notice A record of each accounts delegate mapping (address => address) public delegates; /// @notice A checkpoint for marking number of votes from a given block struct Checkpoint { uint32 fromBlock; uint96 votes; } /// @notice A record of votes checkpoints for each account, by index mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; /// @notice The number of checkpoints for each account mapping (address => uint32) public numCheckpoints; /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the delegation struct used by the contract bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); /// @notice A record of states for signing / validating signatures mapping (address => uint) public nonces; /// @notice An event thats emitted when an account changes its delegate event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); /// @notice An event thats emitted when a delegate account's vote balance changes event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); /// @notice The standard EIP-20 transfer event event Transfer(address indexed from, address indexed to, uint256 amount); /// @notice The standard EIP-20 approval event event Approval(address indexed owner, address indexed spender, uint256 amount); /** * @notice Construct a new Comp token * @param account The initial account to grant all the tokens */ constructor(address account) public { balances[account] = uint96(totalSupply); emit Transfer(address(0), account, totalSupply); } /** * @notice Get the number of tokens `spender` is approved to spend on behalf of `account` * @param account The address of the account holding the funds * @param spender The address of the account spending the funds * @return The number of tokens approved */ function allowance(address account, address spender) external view returns (uint) { return allowances[account][spender]; } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param rawAmount The number of tokens that are approved (2^256-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint rawAmount) external returns (bool) { uint96 amount; if (rawAmount == type(uint).max) { amount = type(uint96).max; } else { amount = safe96(rawAmount, "Comp::approve: amount exceeds 96 bits"); } allowances[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } /** * @notice Get the number of tokens held by the `account` * @param account The address of the account to get the balance of * @return The number of tokens held */ function balanceOf(address account) external view returns (uint) { return balances[account]; } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint rawAmount) external returns (bool) { uint96 amount = safe96(rawAmount, "Comp::transfer: amount exceeds 96 bits"); _transferTokens(msg.sender, dst, amount); return true; } /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint rawAmount) external returns (bool) { address spender = msg.sender; uint96 spenderAllowance = allowances[src][spender]; uint96 amount = safe96(rawAmount, "Comp::approve: amount exceeds 96 bits"); if (spender != src && spenderAllowance != type(uint96).max) { uint96 newAllowance = sub96(spenderAllowance, amount, "Comp::transferFrom: transfer amount exceeds spender allowance"); allowances[src][spender] = newAllowance; emit Approval(src, spender, newAllowance); } _transferTokens(src, dst, amount); return true; } /** * @notice Delegate votes from `msg.sender` to `delegatee` * @param delegatee The address to delegate votes to */ function delegate(address delegatee) public { return _delegate(msg.sender, delegatee); } /** * @notice Delegates votes from signatory to `delegatee` * @param delegatee The address to delegate votes to * @param nonce The contract state required to match the signature * @param expiry The time at which to expire the signature * @param v The recovery byte of the signature * @param r Half of the ECDSA signature pair * @param s Half of the ECDSA signature pair */ function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public { bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "Comp::delegateBySig: invalid signature"); require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce"); require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired"); return _delegate(signatory, delegatee); } /** * @notice Gets the current votes balance for `account` * @param account The address to get votes balance * @return The number of current votes for `account` */ function getCurrentVotes(address account) external view returns (uint96) { uint32 nCheckpoints = numCheckpoints[account]; return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; } /** * @notice Determine the prior number of votes for an account as of a block number * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. * @param account The address of the account to check * @param blockNumber The block number to get the vote balance at * @return The number of votes the account had as of the given block */ function getPriorVotes(address account, uint blockNumber) public view returns (uint96) { require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined"); uint32 nCheckpoints = numCheckpoints[account]; if (nCheckpoints == 0) { return 0; } // First check most recent balance if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { return checkpoints[account][nCheckpoints - 1].votes; } // Next check implicit zero balance if (checkpoints[account][0].fromBlock > blockNumber) { return 0; } uint32 lower = 0; uint32 upper = nCheckpoints - 1; while (upper > lower) { uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Checkpoint memory cp = checkpoints[account][center]; if (cp.fromBlock == blockNumber) { return cp.votes; } else if (cp.fromBlock < blockNumber) { lower = center; } else { upper = center - 1; } } return checkpoints[account][lower].votes; } function _delegate(address delegator, address delegatee) internal { address currentDelegate = delegates[delegator]; uint96 delegatorBalance = balances[delegator]; delegates[delegator] = delegatee; emit DelegateChanged(delegator, currentDelegate, delegatee); _moveDelegates(currentDelegate, delegatee, delegatorBalance); } function _transferTokens(address src, address dst, uint96 amount) internal { require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address"); require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address"); balances[src] = sub96(balances[src], amount, "Comp::_transferTokens: transfer amount exceeds balance"); balances[dst] = add96(balances[dst], amount, "Comp::_transferTokens: transfer amount overflows"); emit Transfer(src, dst, amount); _moveDelegates(delegates[src], delegates[dst], amount); } function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal { if (srcRep != dstRep && amount > 0) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; uint96 srcRepNew = sub96(srcRepOld, amount, "Comp::_moveVotes: vote amount underflows"); _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); } if (dstRep != address(0)) { uint32 dstRepNum = numCheckpoints[dstRep]; uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; uint96 dstRepNew = add96(dstRepOld, amount, "Comp::_moveVotes: vote amount overflows"); _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); } } } function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal { uint32 blockNumber = safe32(block.number, "Comp::_writeCheckpoint: block number exceeds 32 bits"); if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; } else { checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); numCheckpoints[delegatee] = nCheckpoints + 1; } emit DelegateVotesChanged(delegatee, oldVotes, newVotes); } function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } function safe96(uint n, string memory errorMessage) internal pure returns (uint96) { require(n < 2**96, errorMessage); return uint96(n); } function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { uint96 c = a + b; require(c >= a, errorMessage); return c; } function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { require(b <= a, errorMessage); return a - b; } function getChainId() internal view returns (uint) { uint256 chainId; assembly { chainId := chainid() } return chainId; } }
MoaiLensV2.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./MoaiLens.sol"; import "../ExponentialNoError.sol"; contract MoaiLensV2 is MoaiLens, ExponentialNoError { /** * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. * Note that `cTokenBalance` is the number of cTokens the account owns in the market, * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { uint sumCollateral; uint sumBorrowPlusEffects; uint cTokenBalance; uint borrowBalance; uint exchangeRateMantissa; uint oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; Exp tokensToDenom; } function getHealthFactor( ComptrollerLensInterface comptroller, address account ) public view returns (uint) { AccountLiquidityLocalVars memory vars; // Holds all our calculation results uint oErr; PriceOracle oracle = comptroller.oracle(); // For each asset the account is in CToken[] memory assets = comptroller.getAssetsIn(account); for (uint i = 0; i < assets.length; i++) { CToken asset = assets[i]; (, uint collateralFactorMantissa) = comptroller.markets( address(asset) ); // Read the balances and exchange rate from the cToken ( oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa ) = asset.getAccountSnapshot(account); if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return 0; } vars.collateralFactor = Exp({mantissa: collateralFactorMantissa}); vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); // Get the normalized price of the asset vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); if (vars.oraclePriceMantissa == 0) { return 0; } vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); // Pre-compute a conversion factor from tokens -> ether (normalized price value) vars.tokensToDenom = mul_( mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice ); // sumCollateral += tokensToDenom * cTokenBalance // This is the numerator of Health Factor vars.sumCollateral = mul_ScalarTruncateAddUInt( vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral ); // sumBorrowPlusEffects += oraclePrice * borrowBalance vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects ); } return ( vars.sumBorrowPlusEffects > 0 ? div_(vars.sumCollateral * 1e4, vars.sumBorrowPlusEffects) : type(uint).max ); } }
CTokenInterfaces.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./ComptrollerInterface.sol"; import "./InterestRateModel.sol"; import "./EIP20NonStandardInterface.sol"; import "./ErrorReporter.sol"; contract CTokenStorage { /** * @dev Guard variable for re-entrancy checks */ bool internal _notEntered; /** * @notice EIP-20 token name for this token */ string public name; /** * @notice EIP-20 token symbol for this token */ string public symbol; /** * @notice EIP-20 token decimals for this token */ uint8 public decimals; // Maximum borrow rate that can ever be applied (.00014% / block) // TRN's borrowRateMaxMantissa = 0.00014e16 (~= 0.0005/(15/4)) ETH's borrowRateMaxMantissa = 0.0005e16 uint internal constant borrowRateMaxMantissa = 0.00014e16; // Maximum fraction of interest that can be set aside for reserves uint internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-cToken operations */ ComptrollerInterface public comptroller; /** * @notice Model which tells what the current interest rate should be */ InterestRateModel public interestRateModel; // Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) uint internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ uint public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ uint public accrualBlockNumber; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint public totalReserves; /** * @notice Total number of tokens in circulation */ uint public totalSupply; // Official record of token balances for each account mapping(address => uint) internal accountTokens; // Approved token transfer amounts on behalf of others mapping(address => mapping(address => uint)) internal transferAllowances; /** * @notice Container for borrow balance information * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { uint principal; uint interestIndex; } // Mapping of account addresses to outstanding borrow balances mapping(address => BorrowSnapshot) internal accountBorrows; /** * @notice Share of seized collateral that is added to reserves */ uint public constant protocolSeizeShareMantissa = 2.8e16; //2.8% } abstract contract CTokenInterface is CTokenStorage { /** * @notice Indicator that this is a CToken contract (for inspection) */ bool public constant isCToken = true; /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest( uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows ); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint mintAmount, uint mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow( address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows ); /** * @notice Event emitted when a borrow is repaid */ event RepayBorrow( address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows ); /** * @notice Event emitted when a borrow is liquidated */ event LiquidateBorrow( address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens ); /*** Admin Events ***/ /** * @notice Event emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Event emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); /** * @notice Event emitted when comptroller is changed */ event NewComptroller( ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller ); /** * @notice Event emitted when interestRateModel is changed */ event NewMarketInterestRateModel( InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel ); /** * @notice Event emitted when the reserve factor is changed */ event NewReserveFactor( uint oldReserveFactorMantissa, uint newReserveFactorMantissa ); /** * @notice Event emitted when the reserves are added */ event ReservesAdded( address benefactor, uint addAmount, uint newTotalReserves ); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced( address admin, uint reduceAmount, uint newTotalReserves ); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint amount); /** * @notice EIP20 Approval event */ event Approval(address indexed owner, address indexed spender, uint amount); /*** User Interface ***/ function transfer(address dst, uint amount) external virtual returns (bool); function transferFrom( address src, address dst, uint amount ) external virtual returns (bool); function approve( address spender, uint amount ) external virtual returns (bool); function allowance( address owner, address spender ) external view virtual returns (uint); function balanceOf(address owner) external view virtual returns (uint); function balanceOfUnderlying(address owner) external virtual returns (uint); function getAccountSnapshot( address account ) external view virtual returns (uint, uint, uint, uint); function borrowRatePerBlock() external view virtual returns (uint); function supplyRatePerBlock() external view virtual returns (uint); function totalBorrowsCurrent() external virtual returns (uint); function borrowBalanceCurrent( address account ) external virtual returns (uint); function borrowBalanceStored( address account ) external view virtual returns (uint); function exchangeRateCurrent() external virtual returns (uint); function exchangeRateStored() external view virtual returns (uint); function getCash() external view virtual returns (uint); function accrueInterest() external virtual returns (uint); function seize( address liquidator, address borrower, uint seizeTokens ) external virtual returns (uint); /*** Admin Functions ***/ function _setPendingAdmin( address payable newPendingAdmin ) external virtual returns (uint); function _acceptAdmin() external virtual returns (uint); function _setComptroller( ComptrollerInterface newComptroller ) external virtual returns (uint); function _setReserveFactor( uint newReserveFactorMantissa ) external virtual returns (uint); function _reduceReserves(uint reduceAmount) external virtual returns (uint); function _setInterestRateModel( InterestRateModel newInterestRateModel ) external virtual returns (uint); } contract CErc20Storage { /** * @notice Underlying asset for this CToken */ address public underlying; } abstract contract CErc20Interface is CErc20Storage { /*** User Interface ***/ function mint(uint mintAmount) external virtual returns (uint); function redeem(uint redeemTokens) external virtual returns (uint); function redeemUnderlying( uint redeemAmount ) external virtual returns (uint); function borrow(uint borrowAmount) external virtual returns (uint); function repayBorrow(uint repayAmount) external virtual returns (uint); function repayBorrowBehalf( address borrower, uint repayAmount ) external virtual returns (uint); function liquidateBorrow( address borrower, uint repayAmount, CTokenInterface cTokenCollateral ) external virtual returns (uint); function sweepToken(EIP20NonStandardInterface token) external virtual; /*** Admin Functions ***/ function _addReserves(uint addAmount) external virtual returns (uint); } contract CDelegationStorage { /** * @notice Implementation address for this contract */ address public implementation; } abstract contract CDelegatorInterface is CDelegationStorage { /** * @notice Emitted when implementation is changed */ event NewImplementation( address oldImplementation, address newImplementation ); /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation( address implementation_, bool allowResign, bytes memory becomeImplementationData ) external virtual; } abstract contract CDelegateInterface is CDelegationStorage { /** * @notice Called by the delegator on a delegate to initialize it for duty * @dev Should revert if any issues arise which make it unfit for delegation * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) external virtual; /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() external virtual; }
InterestRateModel.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title Compound's InterestRateModel Interface * @author Compound */ abstract contract InterestRateModel { /// @notice Indicator that this is an InterestRateModel contract (for inspection) bool public constant isInterestRateModel = true; /** * @notice Calculates the current borrow interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amount of reserves the market has * @return The borrow rate per block (as a percentage, and scaled by 1e18) */ function getBorrowRate(uint cash, uint borrows, uint reserves) virtual external view returns (uint); /** * @notice Calculates the current supply interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amount of reserves the market has * @param reserveFactorMantissa The current reserve factor the market has * @return The supply rate per block (as a percentage, and scaled by 1e18) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) virtual external view returns (uint); }
ExponentialNoError.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title Exponential module for storing fixed-precision decimals * @author Compound * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: * `Exp({mantissa: 5100000000000000000})`. */ contract ExponentialNoError { uint constant expScale = 1e18; uint constant doubleScale = 1e36; uint constant halfExpScale = expScale/2; uint constant mantissaOne = expScale; struct Exp { uint mantissa; } struct Double { uint mantissa; } /** * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 */ function truncate(Exp memory exp) pure internal returns (uint) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / expScale; } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) { Exp memory product = mul_(a, scalar); return truncate(product); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) { Exp memory product = mul_(a, scalar); return add_(truncate(product), addend); } /** * @dev Checks if first Exp is less than second Exp. */ function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { return left.mantissa < right.mantissa; } /** * @dev Checks if left Exp <= right Exp. */ function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) { return left.mantissa <= right.mantissa; } /** * @dev Checks if left Exp > right Exp. */ function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { return left.mantissa > right.mantissa; } /** * @dev returns true if Exp is exactly zero */ function isZeroExp(Exp memory value) pure internal returns (bool) { return value.mantissa == 0; } function safe224(uint n, string memory errorMessage) pure internal returns (uint224) { require(n < 2**224, errorMessage); return uint224(n); } function safe32(uint n, string memory errorMessage) pure internal returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(uint a, uint b) pure internal returns (uint) { return a + b; } function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(uint a, uint b) pure internal returns (uint) { return a - b; } function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale}); } function mul_(Exp memory a, uint b) pure internal returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b)}); } function mul_(uint a, Exp memory b) pure internal returns (uint) { return mul_(a, b.mantissa) / expScale; } function mul_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale}); } function mul_(Double memory a, uint b) pure internal returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b)}); } function mul_(uint a, Double memory b) pure internal returns (uint) { return mul_(a, b.mantissa) / doubleScale; } function mul_(uint a, uint b) pure internal returns (uint) { return a * b; } function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)}); } function div_(Exp memory a, uint b) pure internal returns (Exp memory) { return Exp({mantissa: div_(a.mantissa, b)}); } function div_(uint a, Exp memory b) pure internal returns (uint) { return div_(mul_(a, expScale), b.mantissa); } function div_(Double memory a, Double memory b) pure internal returns (Double memory) { return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)}); } function div_(Double memory a, uint b) pure internal returns (Double memory) { return Double({mantissa: div_(a.mantissa, b)}); } function div_(uint a, Double memory b) pure internal returns (uint) { return div_(mul_(a, doubleScale), b.mantissa); } function div_(uint a, uint b) pure internal returns (uint) { return a / b; } function fraction(uint a, uint b) pure internal returns (Double memory) { return Double({mantissa: div_(mul_(a, doubleScale), b)}); } }
ComptrollerInterface.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; abstract contract ComptrollerInterface { /// @notice Indicator that this is a Comptroller contract (for inspection) bool public constant isComptroller = true; /*** Assets You Are In ***/ function enterMarkets(address[] calldata cTokens) virtual external returns (uint[] memory); function exitMarket(address cToken) virtual external returns (uint); /*** Policy Hooks ***/ function mintAllowed(address cToken, address minter, uint mintAmount) virtual external returns (uint); function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) virtual external; function redeemAllowed(address cToken, address redeemer, uint redeemTokens) virtual external returns (uint); function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) virtual external; function borrowAllowed(address cToken, address borrower, uint borrowAmount) virtual external returns (uint); function borrowVerify(address cToken, address borrower, uint borrowAmount) virtual external; function repayBorrowAllowed( address cToken, address payer, address borrower, uint repayAmount) virtual external returns (uint); function repayBorrowVerify( address cToken, address payer, address borrower, uint repayAmount, uint borrowerIndex) virtual external; function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount) virtual external returns (uint); function liquidateBorrowVerify( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint repayAmount, uint seizeTokens) virtual external; function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) virtual external returns (uint); function seizeVerify( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint seizeTokens) virtual external; function transferAllowed(address cToken, address src, address dst, uint transferTokens) virtual external returns (uint); function transferVerify(address cToken, address src, address dst, uint transferTokens) virtual external; /*** Liquidity/Liquidation Calculations ***/ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint repayAmount) virtual external view returns (uint, uint); }
GovernorAlpha.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; contract GovernorAlpha { /// @notice The name of this contract string public constant name = "Compound Governor Alpha"; /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed function quorumVotes() public pure returns (uint) { return 400000e18; } // 400,000 = 4% of Comp /// @notice The number of votes required in order for a voter to become a proposer function proposalThreshold() public pure returns (uint) { return 100000e18; } // 100,000 = 1% of Comp /// @notice The maximum number of actions that can be included in a proposal function proposalMaxOperations() public pure returns (uint) { return 10; } // 10 actions /// @notice The delay before voting on a proposal may take place, once proposed function votingDelay() public pure returns (uint) { return 1; } // 1 block /// @notice The duration of voting on a proposal, in blocks function votingPeriod() virtual public pure returns (uint) { return 17280; } // ~3 days in blocks (assuming 15s blocks) /// @notice The address of the Compound Protocol Timelock TimelockInterface public timelock; /// @notice The address of the Compound governance token CompInterface public comp; /// @notice The address of the Governor Guardian address public guardian; /// @notice The total number of proposals uint public proposalCount; struct Proposal { /// @notice Unique id for looking up a proposal uint id; /// @notice Creator of the proposal address proposer; /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds uint eta; /// @notice the ordered list of target addresses for calls to be made address[] targets; /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made uint[] values; /// @notice The ordered list of function signatures to be called string[] signatures; /// @notice The ordered list of calldata to be passed to each call bytes[] calldatas; /// @notice The block at which voting begins: holders must delegate their votes prior to this block uint startBlock; /// @notice The block at which voting ends: votes must be cast prior to this block uint endBlock; /// @notice Current number of votes in favor of this proposal uint forVotes; /// @notice Current number of votes in opposition to this proposal uint againstVotes; /// @notice Flag marking whether the proposal has been canceled bool canceled; /// @notice Flag marking whether the proposal has been executed bool executed; /// @notice Receipts of ballots for the entire set of voters mapping (address => Receipt) receipts; } /// @notice Ballot receipt record for a voter struct Receipt { /// @notice Whether or not a vote has been cast bool hasVoted; /// @notice Whether or not the voter supports the proposal bool support; /// @notice The number of votes the voter had, which were cast uint96 votes; } /// @notice Possible states that a proposal may be in enum ProposalState { Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, Executed } /// @notice The official record of all proposals ever proposed mapping (uint => Proposal) public proposals; /// @notice The latest proposal for each proposer mapping (address => uint) public latestProposalIds; /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the ballot struct used by the contract bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); /// @notice An event emitted when a new proposal is created event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description); /// @notice An event emitted when a vote has been cast on a proposal event VoteCast(address voter, uint proposalId, bool support, uint votes); /// @notice An event emitted when a proposal has been canceled event ProposalCanceled(uint id); /// @notice An event emitted when a proposal has been queued in the Timelock event ProposalQueued(uint id, uint eta); /// @notice An event emitted when a proposal has been executed in the Timelock event ProposalExecuted(uint id); constructor(address timelock_, address comp_, address guardian_) public { timelock = TimelockInterface(timelock_); comp = CompInterface(comp_); guardian = guardian_; } function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) public returns (uint) { require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold"); require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch"); require(targets.length != 0, "GovernorAlpha::propose: must provide actions"); require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions"); uint latestProposalId = latestProposalIds[msg.sender]; if (latestProposalId != 0) { ProposalState proposersLatestProposalState = state(latestProposalId); require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal"); require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal"); } uint startBlock = add256(block.number, votingDelay()); uint endBlock = add256(startBlock, votingPeriod()); proposalCount++; uint proposalId = proposalCount; Proposal storage newProposal = proposals[proposalId]; // This should never happen but add a check in case. require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion"); newProposal.id = proposalId; newProposal.proposer = msg.sender; newProposal.eta = 0; newProposal.targets = targets; newProposal.values = values; newProposal.signatures = signatures; newProposal.calldatas = calldatas; newProposal.startBlock = startBlock; newProposal.endBlock = endBlock; newProposal.forVotes = 0; newProposal.againstVotes = 0; newProposal.canceled = false; newProposal.executed = false; latestProposalIds[newProposal.proposer] = newProposal.id; emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, startBlock, endBlock, description); return newProposal.id; } function queue(uint proposalId) public { require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded"); Proposal storage proposal = proposals[proposalId]; uint eta = add256(block.timestamp, timelock.delay()); for (uint i = 0; i < proposal.targets.length; i++) { _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta); } proposal.eta = eta; emit ProposalQueued(proposalId, eta); } function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"); timelock.queueTransaction(target, value, signature, data, eta); } function execute(uint proposalId) public payable { require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued"); Proposal storage proposal = proposals[proposalId]; proposal.executed = true; for (uint i = 0; i < proposal.targets.length; i++) { timelock.executeTransaction{value: proposal.values[i]}(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta); } emit ProposalExecuted(proposalId); } function cancel(uint proposalId) public { ProposalState state = state(proposalId); require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal"); Proposal storage proposal = proposals[proposalId]; require(msg.sender == guardian || comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold"); proposal.canceled = true; for (uint i = 0; i < proposal.targets.length; i++) { timelock.cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta); } emit ProposalCanceled(proposalId); } function getActions(uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) { Proposal storage p = proposals[proposalId]; return (p.targets, p.values, p.signatures, p.calldatas); } function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) { return proposals[proposalId].receipts[voter]; } function state(uint proposalId) public view returns (ProposalState) { require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); Proposal storage proposal = proposals[proposalId]; if (proposal.canceled) { return ProposalState.Canceled; } else if (block.number <= proposal.startBlock) { return ProposalState.Pending; } else if (block.number <= proposal.endBlock) { return ProposalState.Active; } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) { return ProposalState.Defeated; } else if (proposal.eta == 0) { return ProposalState.Succeeded; } else if (proposal.executed) { return ProposalState.Executed; } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) { return ProposalState.Expired; } else { return ProposalState.Queued; } } function castVote(uint proposalId, bool support) public { return _castVote(msg.sender, proposalId, support); } function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); return _castVote(signatory, proposalId, support); } function _castVote(address voter, uint proposalId, bool support) internal { require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed"); Proposal storage proposal = proposals[proposalId]; Receipt storage receipt = proposal.receipts[voter]; require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted"); uint96 votes = comp.getPriorVotes(voter, proposal.startBlock); if (support) { proposal.forVotes = add256(proposal.forVotes, votes); } else { proposal.againstVotes = add256(proposal.againstVotes, votes); } receipt.hasVoted = true; receipt.support = support; receipt.votes = votes; emit VoteCast(voter, proposalId, support, votes); } function __acceptAdmin() public { require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian"); timelock.acceptAdmin(); } function __abdicate() public { require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian"); guardian = address(0); } function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian"); timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); } function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian"); timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); } function add256(uint256 a, uint256 b) internal pure returns (uint) { uint c = a + b; require(c >= a, "addition overflow"); return c; } function sub256(uint256 a, uint256 b) internal pure returns (uint) { require(b <= a, "subtraction underflow"); return a - b; } function getChainId() internal view returns (uint) { uint chainId; assembly { chainId := chainid() } return chainId; } } interface TimelockInterface { function delay() external view returns (uint); function GRACE_PERIOD() external view returns (uint); function acceptAdmin() external; function queuedTransactions(bytes32 hash) external view returns (bool); function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32); function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external; function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory); } interface CompInterface { function getPriorVotes(address account, uint blockNumber) external view returns (uint96); }
EIP20NonStandardInterface.sol
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title EIP20NonStandardInterface * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ interface EIP20NonStandardInterface { /** * @notice Get the total number of tokens in circulation * @return The supply of tokens */ function totalSupply() external view returns (uint256); /** * @notice Gets the balance of the specified address * @param owner The address from which the balance will be retrieved * @return balance The balance */ function balanceOf(address owner) external view returns (uint256 balance); /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param amount The number of tokens to transfer */ function transfer(address dst, uint256 amount) external; /// /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /// /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param amount The number of tokens to transfer */ function transferFrom(address src, address dst, uint256 amount) external; /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param amount The number of tokens that are approved * @return success Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @notice Get the current allowance from `owner` for `spender` * @param owner The address of the account which owns the tokens to be spent * @param spender The address of the account which may transfer tokens * @return remaining The number of tokens allowed to be spent */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); }
Gas Token: