├── CErc20.sol ├── CErc20Delegate.sol ├── CErc20Delegator.sol ├── CHT.sol ├── CToken.sol ├── CTokenInterfaces.sol ├── Can.sol ├── CarefulMath.sol ├── ChannelsAnchoredOracle.sol ├── ChannelsOracleConfig.sol ├── Comptroller.sol ├── ComptrollerInterface.sol ├── ComptrollerStorage.sol ├── EIP20Interface.sol ├── EIP20NonStandardInterface.sol ├── ErrorReporter.sol ├── Exponential.sol ├── InterestRateModel.sol ├── Maximillion.sol ├── OpenOracleData.sol ├── OpenOraclePriceData.sol ├── Reservoir.sol ├── SafeMath.sol ├── Unitroller.sol └── WhitePaperInterestRateModel.sol /CErc20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CToken.sol"; 4 | 5 | /** 6 | * @title Channels's CErc20 Contract 7 | * @notice CTokens which wrap an EIP-20 underlying 8 | * @author Channels 9 | */ 10 | contract CErc20 is CToken, CErc20Interface { 11 | /** 12 | * @notice Initialize the new money market 13 | * @param underlying_ The address of the underlying asset 14 | * @param comptroller_ The address of the Comptroller 15 | * @param interestRateModel_ The address of the interest rate model 16 | * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 17 | * @param name_ ERC-20 name of this token 18 | * @param symbol_ ERC-20 symbol of this token 19 | * @param decimals_ ERC-20 decimal precision of this token 20 | */ 21 | function initialize(address underlying_, 22 | ComptrollerInterface comptroller_, 23 | InterestRateModel interestRateModel_, 24 | uint initialExchangeRateMantissa_, 25 | string memory name_, 26 | string memory symbol_, 27 | uint8 decimals_) public { 28 | // CToken initialize does the bulk of the work 29 | super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); 30 | 31 | // Set underlying and sanity check it 32 | underlying = underlying_; 33 | EIP20Interface(underlying).totalSupply(); 34 | } 35 | 36 | /*** User Interface ***/ 37 | 38 | /** 39 | * @notice Sender supplies assets into the market and receives cTokens in exchange 40 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 41 | * @param mintAmount The amount of the underlying asset to supply 42 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 43 | */ 44 | function mint(uint mintAmount) external returns (uint) { 45 | (uint err,) = mintInternal(mintAmount); 46 | return err; 47 | } 48 | 49 | /** 50 | * @notice Sender redeems cTokens in exchange for the underlying asset 51 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 52 | * @param redeemTokens The number of cTokens to redeem into underlying 53 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 54 | */ 55 | function redeem(uint redeemTokens) external returns (uint) { 56 | return redeemInternal(redeemTokens); 57 | } 58 | 59 | /** 60 | * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset 61 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 62 | * @param redeemAmount The amount of underlying to redeem 63 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 64 | */ 65 | function redeemUnderlying(uint redeemAmount) external returns (uint) { 66 | return redeemUnderlyingInternal(redeemAmount); 67 | } 68 | 69 | /** 70 | * @notice Sender borrows assets from the protocol to their own address 71 | * @param borrowAmount The amount of the underlying asset to borrow 72 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 73 | */ 74 | function borrow(uint borrowAmount) external returns (uint) { 75 | return borrowInternal(borrowAmount); 76 | } 77 | 78 | /** 79 | * @notice Sender repays their own borrow 80 | * @param repayAmount The amount to repay 81 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 82 | */ 83 | function repayBorrow(uint repayAmount) external returns (uint) { 84 | (uint err,) = repayBorrowInternal(repayAmount); 85 | return err; 86 | } 87 | 88 | /** 89 | * @notice Sender repays a borrow belonging to borrower 90 | * @param borrower the account with the debt being payed off 91 | * @param repayAmount The amount to repay 92 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 93 | */ 94 | function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { 95 | (uint err,) = repayBorrowBehalfInternal(borrower, repayAmount); 96 | return err; 97 | } 98 | 99 | /** 100 | * @notice The sender liquidates the borrowers collateral. 101 | * The collateral seized is transferred to the liquidator. 102 | * @param borrower The borrower of this cToken to be liquidated 103 | * @param repayAmount The amount of the underlying borrowed asset to repay 104 | * @param cTokenCollateral The market in which to seize collateral from the borrower 105 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 106 | */ 107 | function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { 108 | (uint err,) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral); 109 | return err; 110 | } 111 | 112 | /** 113 | * @notice The sender adds to reserves. 114 | * @param addAmount The amount fo underlying token to add as reserves 115 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 116 | */ 117 | function _addReserves(uint addAmount) external returns (uint) { 118 | return _addReservesInternal(addAmount); 119 | } 120 | 121 | /*** Safe Token ***/ 122 | 123 | /** 124 | * @notice Gets balance of this contract in terms of the underlying 125 | * @dev This excludes the value of the current message, if any 126 | * @return The quantity of underlying tokens owned by this contract 127 | */ 128 | function getCashPrior() internal view returns (uint) { 129 | EIP20Interface token = EIP20Interface(underlying); 130 | return token.balanceOf(address(this)); 131 | } 132 | 133 | /** 134 | * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. 135 | * This will revert due to insufficient balance or insufficient allowance. 136 | * This function returns the actual amount received, 137 | * which may be less than `amount` if there is a fee attached to the transfer. 138 | * 139 | * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. 140 | * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 141 | */ 142 | function doTransferIn(address from, uint amount) internal returns (uint) { 143 | EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); 144 | uint balanceBefore = EIP20Interface(underlying).balanceOf(address(this)); 145 | token.transferFrom(from, address(this), amount); 146 | 147 | bool success; 148 | assembly { 149 | switch returndatasize() 150 | case 0 { // This is a non-standard ERC-20 151 | success := not(0) // set success to true 152 | } 153 | case 32 { // This is a compliant ERC-20 154 | returndatacopy(0, 0, 32) 155 | success := mload(0) // Set `success = returndata` of external call 156 | } 157 | default { // This is an excessively non-compliant ERC-20, revert. 158 | revert(0, 0) 159 | } 160 | } 161 | require(success, "TOKEN_TRANSFER_IN_FAILED"); 162 | 163 | // Calculate the amount that was *actually* transferred 164 | uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this)); 165 | require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW"); 166 | return balanceAfter - balanceBefore; // underflow already checked above, just subtract 167 | } 168 | 169 | /** 170 | * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory 171 | * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to 172 | * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified 173 | * it is >= amount, this should not revert in normal conditions. 174 | * 175 | * Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. 176 | * See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 177 | */ 178 | function doTransferOut(address payable to, uint amount) internal { 179 | EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); 180 | token.transfer(to, amount); 181 | 182 | bool success; 183 | assembly { 184 | switch returndatasize() 185 | case 0 { // This is a non-standard ERC-20 186 | success := not(0) // set success to true 187 | } 188 | case 32 { // This is a complaint ERC-20 189 | returndatacopy(0, 0, 32) 190 | success := mload(0) // Set `success = returndata` of external call 191 | } 192 | default { // This is an excessively non-compliant ERC-20, revert. 193 | revert(0, 0) 194 | } 195 | } 196 | require(success, "TOKEN_TRANSFER_OUT_FAILED"); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /CErc20Delegate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CErc20.sol"; 4 | 5 | /** 6 | * @title Channels's CErc20Delegate Contract 7 | * @notice CTokens which wrap an EIP-20 underlying and are delegated to 8 | * @author Channels 9 | */ 10 | contract CErc20Delegate is CErc20, CDelegateInterface { 11 | /** 12 | * @notice Construct an empty delegate 13 | */ 14 | constructor() public {} 15 | 16 | /** 17 | * @notice Called by the delegator on a delegate to initialize it for duty 18 | * @param data The encoded bytes data for any initialization 19 | */ 20 | function _becomeImplementation(bytes memory data) public { 21 | // Shh -- currently unused 22 | data; 23 | 24 | // Shh -- we don't ever want this hook to be marked pure 25 | if (false) { 26 | implementation = address(0); 27 | } 28 | 29 | require(msg.sender == admin, "only the admin may call _becomeImplementation"); 30 | } 31 | 32 | /** 33 | * @notice Called by the delegator on a delegate to forfeit its responsibility 34 | */ 35 | function _resignImplementation() public { 36 | // Shh -- we don't ever want this hook to be marked pure 37 | if (false) { 38 | implementation = address(0); 39 | } 40 | 41 | require(msg.sender == admin, "only the admin may call _resignImplementation"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CErc20Delegator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CTokenInterfaces.sol"; 4 | 5 | /** 6 | * @title Channels's CErc20Delegator Contract 7 | * @notice CTokens which wrap an EIP-20 underlying and delegate to an implementation 8 | * @author Channels 9 | */ 10 | contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterface { 11 | /** 12 | * @notice Construct a new money market 13 | * @param underlying_ The address of the underlying asset 14 | * @param comptroller_ The address of the Comptroller 15 | * @param interestRateModel_ The address of the interest rate model 16 | * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 17 | * @param name_ ERC-20 name of this token 18 | * @param symbol_ ERC-20 symbol of this token 19 | * @param decimals_ ERC-20 decimal precision of this token 20 | * @param admin_ Address of the administrator of this token 21 | * @param implementation_ The address of the implementation the contract delegates to 22 | * @param becomeImplementationData The encoded args for becomeImplementation 23 | */ 24 | constructor(address underlying_, 25 | ComptrollerInterface comptroller_, 26 | InterestRateModel interestRateModel_, 27 | uint initialExchangeRateMantissa_, 28 | string memory name_, 29 | string memory symbol_, 30 | uint8 decimals_, 31 | address payable admin_, 32 | address implementation_, 33 | bytes memory becomeImplementationData) public { 34 | // Creator of the contract is admin during initialization 35 | admin = msg.sender; 36 | 37 | // First delegate gets to initialize the delegator (i.e. storage contract) 38 | delegateTo(implementation_, abi.encodeWithSignature("initialize(address,address,address,uint256,string,string,uint8)", 39 | underlying_, 40 | comptroller_, 41 | interestRateModel_, 42 | initialExchangeRateMantissa_, 43 | name_, 44 | symbol_, 45 | decimals_)); 46 | 47 | // New implementations always get set via the settor (post-initialize) 48 | _setImplementation(implementation_, false, becomeImplementationData); 49 | 50 | // Set the proper admin now that initialization is done 51 | admin = admin_; 52 | } 53 | 54 | /** 55 | * @notice Called by the admin to update the implementation of the delegator 56 | * @param implementation_ The address of the new implementation for delegation 57 | * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation 58 | * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation 59 | */ 60 | function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public { 61 | require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin"); 62 | 63 | if (allowResign) { 64 | delegateToImplementation(abi.encodeWithSignature("_resignImplementation()")); 65 | } 66 | 67 | address oldImplementation = implementation; 68 | implementation = implementation_; 69 | 70 | delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData)); 71 | 72 | emit NewImplementation(oldImplementation, implementation); 73 | } 74 | 75 | /** 76 | * @notice Sender supplies assets into the market and receives cTokens in exchange 77 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 78 | * @param mintAmount The amount of the underlying asset to supply 79 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 80 | */ 81 | function mint(uint mintAmount) external returns (uint) { 82 | mintAmount; // Shh 83 | delegateAndReturn(); 84 | } 85 | 86 | /** 87 | * @notice Sender redeems cTokens in exchange for the underlying asset 88 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 89 | * @param redeemTokens The number of cTokens to redeem into underlying 90 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 91 | */ 92 | function redeem(uint redeemTokens) external returns (uint) { 93 | redeemTokens; // Shh 94 | delegateAndReturn(); 95 | } 96 | 97 | /** 98 | * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset 99 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 100 | * @param redeemAmount The amount of underlying to redeem 101 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 102 | */ 103 | function redeemUnderlying(uint redeemAmount) external returns (uint) { 104 | redeemAmount; // Shh 105 | delegateAndReturn(); 106 | } 107 | 108 | /** 109 | * @notice Sender borrows assets from the protocol to their own address 110 | * @param borrowAmount The amount of the underlying asset to borrow 111 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 112 | */ 113 | function borrow(uint borrowAmount) external returns (uint) { 114 | borrowAmount; // Shh 115 | delegateAndReturn(); 116 | } 117 | 118 | /** 119 | * @notice Sender repays their own borrow 120 | * @param repayAmount The amount to repay 121 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 122 | */ 123 | function repayBorrow(uint repayAmount) external returns (uint) { 124 | repayAmount; // Shh 125 | delegateAndReturn(); 126 | } 127 | 128 | /** 129 | * @notice Sender repays a borrow belonging to borrower 130 | * @param borrower the account with the debt being payed off 131 | * @param repayAmount The amount to repay 132 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 133 | */ 134 | function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) { 135 | borrower; repayAmount; // Shh 136 | delegateAndReturn(); 137 | } 138 | 139 | /** 140 | * @notice The sender liquidates the borrowers collateral. 141 | * The collateral seized is transferred to the liquidator. 142 | * @param borrower The borrower of this cToken to be liquidated 143 | * @param cTokenCollateral The market in which to seize collateral from the borrower 144 | * @param repayAmount The amount of the underlying borrowed asset to repay 145 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 146 | */ 147 | function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) { 148 | borrower; repayAmount; cTokenCollateral; // Shh 149 | delegateAndReturn(); 150 | } 151 | 152 | /** 153 | * @notice Transfer `amount` tokens from `msg.sender` to `dst` 154 | * @param dst The address of the destination account 155 | * @param amount The number of tokens to transfer 156 | * @return Whether or not the transfer succeeded 157 | */ 158 | function transfer(address dst, uint amount) external returns (bool) { 159 | dst; amount; // Shh 160 | delegateAndReturn(); 161 | } 162 | 163 | /** 164 | * @notice Transfer `amount` tokens from `src` to `dst` 165 | * @param src The address of the source account 166 | * @param dst The address of the destination account 167 | * @param amount The number of tokens to transfer 168 | * @return Whether or not the transfer succeeded 169 | */ 170 | function transferFrom(address src, address dst, uint256 amount) external returns (bool) { 171 | src; dst; amount; // Shh 172 | delegateAndReturn(); 173 | } 174 | 175 | /** 176 | * @notice Approve `spender` to transfer up to `amount` from `src` 177 | * @dev This will overwrite the approval amount for `spender` 178 | * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 179 | * @param spender The address of the account which may transfer tokens 180 | * @param amount The number of tokens that are approved (-1 means infinite) 181 | * @return Whether or not the approval succeeded 182 | */ 183 | function approve(address spender, uint256 amount) external returns (bool) { 184 | spender; amount; // Shh 185 | delegateAndReturn(); 186 | } 187 | 188 | /** 189 | * @notice Get the current allowance from `owner` for `spender` 190 | * @param owner The address of the account which owns the tokens to be spent 191 | * @param spender The address of the account which may transfer tokens 192 | * @return The number of tokens allowed to be spent (-1 means infinite) 193 | */ 194 | function allowance(address owner, address spender) external view returns (uint) { 195 | owner; spender; // Shh 196 | delegateToViewAndReturn(); 197 | } 198 | 199 | /** 200 | * @notice Get the token balance of the `owner` 201 | * @param owner The address of the account to query 202 | * @return The number of tokens owned by `owner` 203 | */ 204 | function balanceOf(address owner) external view returns (uint) { 205 | owner; // Shh 206 | delegateToViewAndReturn(); 207 | } 208 | 209 | /** 210 | * @notice Get the underlying balance of the `owner` 211 | * @dev This also accrues interest in a transaction 212 | * @param owner The address of the account to query 213 | * @return The amount of underlying owned by `owner` 214 | */ 215 | function balanceOfUnderlying(address owner) external returns (uint) { 216 | owner; // Shh 217 | delegateAndReturn(); 218 | } 219 | 220 | /** 221 | * @notice Get a snapshot of the account's balances, and the cached exchange rate 222 | * @dev This is used by comptroller to more efficiently perform liquidity checks. 223 | * @param account Address of the account to snapshot 224 | * @return (possible error, token balance, borrow balance, exchange rate mantissa) 225 | */ 226 | function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) { 227 | account; // Shh 228 | delegateToViewAndReturn(); 229 | } 230 | 231 | /** 232 | * @notice Returns the current per-block borrow interest rate for this cToken 233 | * @return The borrow interest rate per block, scaled by 1e18 234 | */ 235 | function borrowRatePerBlock() external view returns (uint) { 236 | delegateToViewAndReturn(); 237 | } 238 | 239 | /** 240 | * @notice Returns the current per-block supply interest rate for this cToken 241 | * @return The supply interest rate per block, scaled by 1e18 242 | */ 243 | function supplyRatePerBlock() external view returns (uint) { 244 | delegateToViewAndReturn(); 245 | } 246 | 247 | /** 248 | * @notice Returns the current total borrows plus accrued interest 249 | * @return The total borrows with interest 250 | */ 251 | function totalBorrowsCurrent() external returns (uint) { 252 | delegateAndReturn(); 253 | } 254 | 255 | /** 256 | * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex 257 | * @param account The address whose balance should be calculated after updating borrowIndex 258 | * @return The calculated balance 259 | */ 260 | function borrowBalanceCurrent(address account) external returns (uint) { 261 | account; // Shh 262 | delegateAndReturn(); 263 | } 264 | 265 | /** 266 | * @notice Return the borrow balance of account based on stored data 267 | * @param account The address whose balance should be calculated 268 | * @return The calculated balance 269 | */ 270 | function borrowBalanceStored(address account) public view returns (uint) { 271 | account; // Shh 272 | delegateToViewAndReturn(); 273 | } 274 | 275 | /** 276 | * @notice Accrue interest then return the up-to-date exchange rate 277 | * @return Calculated exchange rate scaled by 1e18 278 | */ 279 | function exchangeRateCurrent() public returns (uint) { 280 | delegateAndReturn(); 281 | } 282 | 283 | /** 284 | * @notice Calculates the exchange rate from the underlying to the CToken 285 | * @dev This function does not accrue interest before calculating the exchange rate 286 | * @return Calculated exchange rate scaled by 1e18 287 | */ 288 | function exchangeRateStored() public view returns (uint) { 289 | delegateToViewAndReturn(); 290 | } 291 | 292 | /** 293 | * @notice Get cash balance of this cToken in the underlying asset 294 | * @return The quantity of underlying asset owned by this contract 295 | */ 296 | function getCash() external view returns (uint) { 297 | delegateToViewAndReturn(); 298 | } 299 | 300 | /** 301 | * @notice Applies accrued interest to total borrows and reserves. 302 | * @dev This calculates interest accrued from the last checkpointed block 303 | * up to the current block and writes new checkpoint to storage. 304 | */ 305 | function accrueInterest() public returns (uint) { 306 | delegateAndReturn(); 307 | } 308 | 309 | /** 310 | * @notice Transfers collateral tokens (this market) to the liquidator. 311 | * @dev Will fail unless called by another cToken during the process of liquidation. 312 | * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. 313 | * @param liquidator The account receiving seized collateral 314 | * @param borrower The account having collateral seized 315 | * @param seizeTokens The number of cTokens to seize 316 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 317 | */ 318 | function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint) { 319 | liquidator; borrower; seizeTokens; // Shh 320 | delegateAndReturn(); 321 | } 322 | 323 | /*** Admin Functions ***/ 324 | 325 | /** 326 | * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. 327 | * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. 328 | * @param newPendingAdmin New pending admin. 329 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 330 | */ 331 | function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) { 332 | newPendingAdmin; // Shh 333 | delegateAndReturn(); 334 | } 335 | 336 | /** 337 | * @notice Sets a new comptroller for the market 338 | * @dev Admin function to set a new comptroller 339 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 340 | */ 341 | function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) { 342 | newComptroller; // Shh 343 | delegateAndReturn(); 344 | } 345 | 346 | /** 347 | * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh 348 | * @dev Admin function to accrue interest and set a new reserve factor 349 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 350 | */ 351 | function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint) { 352 | newReserveFactorMantissa; // Shh 353 | delegateAndReturn(); 354 | } 355 | 356 | /** 357 | * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin 358 | * @dev Admin function for pending admin to accept role and update admin 359 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 360 | */ 361 | function _acceptAdmin() external returns (uint) { 362 | delegateAndReturn(); 363 | } 364 | 365 | /** 366 | * @notice Accrues interest and adds reserves by transferring from admin 367 | * @param addAmount Amount of reserves to add 368 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 369 | */ 370 | function _addReserves(uint addAmount) external returns (uint) { 371 | addAmount; // Shh 372 | delegateAndReturn(); 373 | } 374 | 375 | /** 376 | * @notice Accrues interest and reduces reserves by transferring to admin 377 | * @param reduceAmount Amount of reduction to reserves 378 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 379 | */ 380 | function _reduceReserves(uint reduceAmount) external returns (uint) { 381 | reduceAmount; // Shh 382 | delegateAndReturn(); 383 | } 384 | 385 | /** 386 | * @notice Accrues interest and updates the interest rate model using _setInterestRateModelFresh 387 | * @dev Admin function to accrue interest and update the interest rate model 388 | * @param newInterestRateModel the new interest rate model to use 389 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 390 | */ 391 | function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) { 392 | newInterestRateModel; // Shh 393 | delegateAndReturn(); 394 | } 395 | 396 | /** 397 | * @notice Internal method to delegate execution to another contract 398 | * @dev It returns to the external caller whatever the implementation returns or forwards reverts 399 | * @param callee The contract to delegatecall 400 | * @param data The raw data to delegatecall 401 | * @return The returned bytes from the delegatecall 402 | */ 403 | function delegateTo(address callee, bytes memory data) internal returns (bytes memory) { 404 | (bool success, bytes memory returnData) = callee.delegatecall(data); 405 | assembly { 406 | if eq(success, 0) { 407 | revert(add(returnData, 0x20), returndatasize) 408 | } 409 | } 410 | return returnData; 411 | } 412 | 413 | /** 414 | * @notice Delegates execution to the implementation contract 415 | * @dev It returns to the external caller whatever the implementation returns or forwards reverts 416 | * @param data The raw data to delegatecall 417 | * @return The returned bytes from the delegatecall 418 | */ 419 | function delegateToImplementation(bytes memory data) public returns (bytes memory) { 420 | return delegateTo(implementation, data); 421 | } 422 | 423 | /** 424 | * @notice Delegates execution to an implementation contract 425 | * @dev It returns to the external caller whatever the implementation returns or forwards reverts 426 | * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. 427 | * @param data The raw data to delegatecall 428 | * @return The returned bytes from the delegatecall 429 | */ 430 | function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { 431 | (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data)); 432 | assembly { 433 | if eq(success, 0) { 434 | revert(add(returnData, 0x20), returndatasize) 435 | } 436 | } 437 | return abi.decode(returnData, (bytes)); 438 | } 439 | 440 | function delegateToViewAndReturn() private view returns (bytes memory) { 441 | (bool success, ) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data)); 442 | 443 | assembly { 444 | let free_mem_ptr := mload(0x40) 445 | returndatacopy(free_mem_ptr, 0, returndatasize) 446 | 447 | switch success 448 | case 0 { revert(free_mem_ptr, returndatasize) } 449 | default { return(add(free_mem_ptr, 0x40), returndatasize) } 450 | } 451 | } 452 | 453 | function delegateAndReturn() private returns (bytes memory) { 454 | (bool success, ) = implementation.delegatecall(msg.data); 455 | 456 | assembly { 457 | let free_mem_ptr := mload(0x40) 458 | returndatacopy(free_mem_ptr, 0, returndatasize) 459 | 460 | switch success 461 | case 0 { revert(free_mem_ptr, returndatasize) } 462 | default { return(free_mem_ptr, returndatasize) } 463 | } 464 | } 465 | 466 | /** 467 | * @notice Delegates execution to an implementation contract 468 | * @dev It returns to the external caller whatever the implementation returns or forwards reverts 469 | */ 470 | function () external payable { 471 | require(msg.value == 0,"CErc20Delegator:fallback: cannot send value to fallback"); 472 | 473 | // delegate all other functions to current implementation 474 | delegateAndReturn(); 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /CHT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CToken.sol"; 4 | 5 | /** 6 | * @title Channels's CHT Contract 7 | * @notice CToken which wraps HT 8 | * @author Channels 9 | */ 10 | contract CHT is CToken { 11 | /** 12 | * @notice Construct a new CHT money market 13 | * @param comptroller_ The address of the Comptroller 14 | * @param interestRateModel_ The address of the interest rate model 15 | * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 16 | * @param name_ ERC-20 name of this token 17 | * @param symbol_ ERC-20 symbol of this token 18 | * @param decimals_ ERC-20 decimal precision of this token 19 | * @param admin_ Address of the administrator of this token 20 | */ 21 | constructor(ComptrollerInterface comptroller_, 22 | InterestRateModel interestRateModel_, 23 | uint initialExchangeRateMantissa_, 24 | string memory name_, 25 | string memory symbol_, 26 | uint8 decimals_, 27 | address payable admin_) public { 28 | // Creator of the contract is admin during initialization 29 | admin = msg.sender; 30 | 31 | initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); 32 | 33 | // Set the proper admin now that initialization is done 34 | admin = admin_; 35 | } 36 | 37 | 38 | /*** User Interface ***/ 39 | 40 | /** 41 | * @notice Sender supplies assets into the market and receives cTokens in exchange 42 | * @dev Reverts upon any failure 43 | */ 44 | function mint() external payable { 45 | (uint err,) = mintInternal(msg.value); 46 | requireNoError(err, "mint failed"); 47 | } 48 | 49 | /** 50 | * @notice Sender redeems cTokens in exchange for the underlying asset 51 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 52 | * @param redeemTokens The number of cTokens to redeem into underlying 53 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 54 | */ 55 | function redeem(uint redeemTokens) external returns (uint) { 56 | return redeemInternal(redeemTokens); 57 | } 58 | 59 | /** 60 | * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset 61 | * @dev Accrues interest whether or not the operation succeeds, unless reverted 62 | * @param redeemAmount The amount of underlying to redeem 63 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 64 | */ 65 | function redeemUnderlying(uint redeemAmount) external returns (uint) { 66 | return redeemUnderlyingInternal(redeemAmount); 67 | } 68 | 69 | /** 70 | * @notice Sender borrows assets from the protocol to their own address 71 | * @param borrowAmount The amount of the underlying asset to borrow 72 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 73 | */ 74 | function borrow(uint borrowAmount) external returns (uint) { 75 | return borrowInternal(borrowAmount); 76 | } 77 | 78 | /** 79 | * @notice Sender repays their own borrow 80 | * @dev Reverts upon any failure 81 | */ 82 | function repayBorrow() external payable { 83 | (uint err,) = repayBorrowInternal(msg.value); 84 | requireNoError(err, "repayBorrow failed"); 85 | } 86 | 87 | /** 88 | * @notice Sender repays a borrow belonging to borrower 89 | * @dev Reverts upon any failure 90 | * @param borrower the account with the debt being payed off 91 | */ 92 | function repayBorrowBehalf(address borrower) external payable { 93 | (uint err,) = repayBorrowBehalfInternal(borrower, msg.value); 94 | requireNoError(err, "repayBorrowBehalf failed"); 95 | } 96 | 97 | /** 98 | * @notice The sender liquidates the borrowers collateral. 99 | * The collateral seized is transferred to the liquidator. 100 | * @dev Reverts upon any failure 101 | * @param borrower The borrower of this cToken to be liquidated 102 | * @param cTokenCollateral The market in which to seize collateral from the borrower 103 | */ 104 | function liquidateBorrow(address borrower, CToken cTokenCollateral) external payable { 105 | (uint err,) = liquidateBorrowInternal(borrower, msg.value, cTokenCollateral); 106 | requireNoError(err, "liquidateBorrow failed"); 107 | } 108 | 109 | /** 110 | * @notice Send HT to CHT to mint 111 | */ 112 | function () external payable { 113 | (uint err,) = mintInternal(msg.value); 114 | requireNoError(err, "mint failed"); 115 | } 116 | 117 | /*** Safe Token ***/ 118 | 119 | /** 120 | * @notice Gets balance of this contract in terms of HT, before this message 121 | * @dev This excludes the value of the current message, if any 122 | * @return The quantity of HT owned by this contract 123 | */ 124 | function getCashPrior() internal view returns (uint) { 125 | (MathError err, uint startingBalance) = subUInt(address(this).balance, msg.value); 126 | require(err == MathError.NO_ERROR); 127 | return startingBalance; 128 | } 129 | 130 | /** 131 | * @notice Perform the actual transfer in, which is a no-op 132 | * @param from Address sending the HT 133 | * @param amount Amount of HT being sent 134 | * @return The actual amount of HT transferred 135 | */ 136 | function doTransferIn(address from, uint amount) internal returns (uint) { 137 | // Sanity checks 138 | require(msg.sender == from, "sender mismatch"); 139 | require(msg.value == amount, "value mismatch"); 140 | return amount; 141 | } 142 | 143 | function doTransferOut(address payable to, uint amount) internal { 144 | /* Send the HT, with minimal gas and revert on failure */ 145 | to.transfer(amount); 146 | } 147 | 148 | function requireNoError(uint errCode, string memory message) internal pure { 149 | if (errCode == uint(Error.NO_ERROR)) { 150 | return; 151 | } 152 | 153 | bytes memory fullMessage = new bytes(bytes(message).length + 5); 154 | uint i; 155 | 156 | for (i = 0; i < bytes(message).length; i++) { 157 | fullMessage[i] = bytes(message)[i]; 158 | } 159 | 160 | fullMessage[i+0] = byte(uint8(32)); 161 | fullMessage[i+1] = byte(uint8(40)); 162 | fullMessage[i+2] = byte(uint8(48 + ( errCode / 10 ))); 163 | fullMessage[i+3] = byte(uint8(48 + ( errCode % 10 ))); 164 | fullMessage[i+4] = byte(uint8(41)); 165 | 166 | require(errCode == uint(Error.NO_ERROR), string(fullMessage)); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /CTokenInterfaces.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./ComptrollerInterface.sol"; 4 | import "./InterestRateModel.sol"; 5 | 6 | contract CTokenStorage { 7 | /** 8 | * @dev Guard variable for re-entrancy checks 9 | */ 10 | bool internal _notEntered; 11 | 12 | /** 13 | * @notice EIP-20 token name for this token 14 | */ 15 | string public name; 16 | 17 | /** 18 | * @notice EIP-20 token symbol for this token 19 | */ 20 | string public symbol; 21 | 22 | /** 23 | * @notice EIP-20 token decimals for this token 24 | */ 25 | uint8 public decimals; 26 | 27 | /** 28 | * @notice Maximum borrow rate that can ever be applied (.0005% / block) 29 | */ 30 | 31 | uint internal constant borrowRateMaxMantissa = 0.0005e16; 32 | 33 | /** 34 | * @notice Maximum fraction of interest that can be set aside for reserves 35 | */ 36 | uint internal constant reserveFactorMaxMantissa = 1e18; 37 | 38 | /** 39 | * @notice Administrator for this contract 40 | */ 41 | address payable public admin; 42 | 43 | /** 44 | * @notice Pending administrator for this contract 45 | */ 46 | address payable public pendingAdmin; 47 | 48 | /** 49 | * @notice Contract which oversees inter-cToken operations 50 | */ 51 | ComptrollerInterface public comptroller; 52 | 53 | /** 54 | * @notice Model which tells what the current interest rate should be 55 | */ 56 | InterestRateModel public interestRateModel; 57 | 58 | /** 59 | * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) 60 | */ 61 | uint internal initialExchangeRateMantissa; 62 | 63 | /** 64 | * @notice Fraction of interest currently set aside for reserves 65 | */ 66 | uint public reserveFactorMantissa; 67 | 68 | /** 69 | * @notice Block number that interest was last accrued at 70 | */ 71 | uint public accrualBlockNumber; 72 | 73 | /** 74 | * @notice Accumulator of the total earned interest rate since the opening of the market 75 | */ 76 | uint public borrowIndex; 77 | 78 | /** 79 | * @notice Total amount of outstanding borrows of the underlying in this market 80 | */ 81 | uint public totalBorrows; 82 | 83 | /** 84 | * @notice Total amount of reserves of the underlying held in this market 85 | */ 86 | uint public totalReserves; 87 | 88 | /** 89 | * @notice Total number of tokens in circulation 90 | */ 91 | uint public totalSupply; 92 | 93 | /** 94 | * @notice Official record of token balances for each account 95 | */ 96 | mapping (address => uint) internal accountTokens; 97 | 98 | /** 99 | * @notice Approved token transfer amounts on behalf of others 100 | */ 101 | mapping (address => mapping (address => uint)) internal transferAllowances; 102 | 103 | /** 104 | * @notice Container for borrow balance information 105 | * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action 106 | * @member interestIndex Global borrowIndex as of the most recent balance-changing action 107 | */ 108 | struct BorrowSnapshot { 109 | uint principal; 110 | uint interestIndex; 111 | } 112 | 113 | /** 114 | * @notice Mapping of account addresses to outstanding borrow balances 115 | */ 116 | mapping(address => BorrowSnapshot) internal accountBorrows; 117 | } 118 | 119 | contract CTokenInterface is CTokenStorage { 120 | /** 121 | * @notice Indicator that this is a CToken contract (for inspection) 122 | */ 123 | bool public constant isCToken = true; 124 | 125 | 126 | /*** Market Events ***/ 127 | 128 | /** 129 | * @notice Event emitted when interest is accrued 130 | */ 131 | event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); 132 | 133 | /** 134 | * @notice Event emitted when tokens are minted 135 | */ 136 | event Mint(address minter, uint mintAmount, uint mintTokens); 137 | 138 | /** 139 | * @notice Event emitted when tokens are redeemed 140 | */ 141 | event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); 142 | 143 | /** 144 | * @notice Event emitted when underlying is borrowed 145 | */ 146 | event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); 147 | 148 | /** 149 | * @notice Event emitted when a borrow is repaid 150 | */ 151 | event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); 152 | 153 | /** 154 | * @notice Event emitted when a borrow is liquidated 155 | */ 156 | event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); 157 | 158 | 159 | /*** Admin Events ***/ 160 | 161 | /** 162 | * @notice Event emitted when pendingAdmin is changed 163 | */ 164 | event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); 165 | 166 | /** 167 | * @notice Event emitted when pendingAdmin is accepted, which means admin is updated 168 | */ 169 | event NewAdmin(address oldAdmin, address newAdmin); 170 | 171 | /** 172 | * @notice Event emitted when comptroller is changed 173 | */ 174 | event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); 175 | 176 | /** 177 | * @notice Event emitted when interestRateModel is changed 178 | */ 179 | event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); 180 | 181 | /** 182 | * @notice Event emitted when the reserve factor is changed 183 | */ 184 | event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); 185 | 186 | /** 187 | * @notice Event emitted when the reserves are added 188 | */ 189 | event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); 190 | 191 | /** 192 | * @notice Event emitted when the reserves are reduced 193 | */ 194 | event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); 195 | 196 | /** 197 | * @notice EIP20 Transfer event 198 | */ 199 | event Transfer(address indexed from, address indexed to, uint amount); 200 | 201 | /** 202 | * @notice EIP20 Approval event 203 | */ 204 | event Approval(address indexed owner, address indexed spender, uint amount); 205 | 206 | /** 207 | * @notice Failure event 208 | */ 209 | event Failure(uint error, uint info, uint detail); 210 | 211 | 212 | /*** User Interface ***/ 213 | 214 | function transfer(address dst, uint amount) external returns (bool); 215 | function transferFrom(address src, address dst, uint amount) external returns (bool); 216 | function approve(address spender, uint amount) external returns (bool); 217 | function allowance(address owner, address spender) external view returns (uint); 218 | function balanceOf(address owner) external view returns (uint); 219 | function balanceOfUnderlying(address owner) external returns (uint); 220 | function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint); 221 | function borrowRatePerBlock() external view returns (uint); 222 | function supplyRatePerBlock() external view returns (uint); 223 | function totalBorrowsCurrent() external returns (uint); 224 | function borrowBalanceCurrent(address account) external returns (uint); 225 | function borrowBalanceStored(address account) public view returns (uint); 226 | function exchangeRateCurrent() public returns (uint); 227 | function exchangeRateStored() public view returns (uint); 228 | function getCash() external view returns (uint); 229 | function accrueInterest() public returns (uint); 230 | function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint); 231 | 232 | 233 | /*** Admin Functions ***/ 234 | 235 | function _setPendingAdmin(address payable newPendingAdmin) external returns (uint); 236 | function _acceptAdmin() external returns (uint); 237 | function _setComptroller(ComptrollerInterface newComptroller) public returns (uint); 238 | function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint); 239 | function _reduceReserves(uint reduceAmount) external returns (uint); 240 | function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint); 241 | } 242 | 243 | contract CErc20Storage { 244 | /** 245 | * @notice Underlying asset for this CToken 246 | */ 247 | address public underlying; 248 | } 249 | 250 | contract CErc20Interface is CErc20Storage { 251 | 252 | /*** User Interface ***/ 253 | 254 | function mint(uint mintAmount) external returns (uint); 255 | function redeem(uint redeemTokens) external returns (uint); 256 | function redeemUnderlying(uint redeemAmount) external returns (uint); 257 | function borrow(uint borrowAmount) external returns (uint); 258 | function repayBorrow(uint repayAmount) external returns (uint); 259 | function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); 260 | function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint); 261 | 262 | 263 | /*** Admin Functions ***/ 264 | 265 | function _addReserves(uint addAmount) external returns (uint); 266 | } 267 | 268 | contract CDelegationStorage { 269 | /** 270 | * @notice Implementation address for this contract 271 | */ 272 | address public implementation; 273 | } 274 | 275 | contract CDelegatorInterface is CDelegationStorage { 276 | /** 277 | * @notice Emitted when implementation is changed 278 | */ 279 | event NewImplementation(address oldImplementation, address newImplementation); 280 | 281 | /** 282 | * @notice Called by the admin to update the implementation of the delegator 283 | * @param implementation_ The address of the new implementation for delegation 284 | * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation 285 | * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation 286 | */ 287 | function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public; 288 | } 289 | 290 | contract CDelegateInterface is CDelegationStorage { 291 | /** 292 | * @notice Called by the delegator on a delegate to initialize it for duty 293 | * @dev Should revert if any issues arise which make it unfit for delegation 294 | * @param data The encoded bytes data for any initialization 295 | */ 296 | function _becomeImplementation(bytes memory data) public; 297 | 298 | /** 299 | * @notice Called by the delegator on a delegate to forfeit its responsibility 300 | */ 301 | function _resignImplementation() public; 302 | } 303 | -------------------------------------------------------------------------------- /Can.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | pragma experimental ABIEncoderV2; 3 | 4 | contract Can { 5 | /// @notice EIP-20 token name for this token 6 | string public constant name = "Channels"; 7 | 8 | /// @notice EIP-20 token symbol for this token 9 | string public constant symbol = "CAN"; 10 | 11 | /// @notice EIP-20 token decimals for this token 12 | uint8 public constant decimals = 18; 13 | 14 | /// @notice Total number of tokens in circulation 15 | uint public constant totalSupply = 10000000e18; // 10 million Can 16 | 17 | /// @notice Allowance amounts on behalf of others 18 | mapping (address => mapping (address => uint96)) internal allowances; 19 | 20 | /// @notice Official record of token balances for each account 21 | mapping (address => uint96) internal balances; 22 | 23 | /// @notice A record of each accounts delegate 24 | mapping (address => address) public delegates; 25 | 26 | /// @notice A checkpoint for marking number of votes from a given block 27 | struct Checkpoint { 28 | uint32 fromBlock; 29 | uint96 votes; 30 | } 31 | 32 | /// @notice A record of votes checkpoints for each account, by index 33 | mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; 34 | 35 | /// @notice The number of checkpoints for each account 36 | mapping (address => uint32) public numCheckpoints; 37 | 38 | /// @notice The EIP-712 typehash for the contract's domain 39 | bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); 40 | 41 | /// @notice The EIP-712 typehash for the delegation struct used by the contract 42 | bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); 43 | 44 | /// @notice A record of states for signing / validating signatures 45 | mapping (address => uint) public nonces; 46 | 47 | /// @notice An event thats emitted when an account changes its delegate 48 | event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); 49 | 50 | /// @notice An event thats emitted when a delegate account's vote balance changes 51 | event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); 52 | 53 | /// @notice The standard EIP-20 transfer event 54 | event Transfer(address indexed from, address indexed to, uint256 amount); 55 | 56 | /// @notice The standard EIP-20 approval event 57 | event Approval(address indexed owner, address indexed spender, uint256 amount); 58 | 59 | /** 60 | * @notice Construct a new Can token 61 | * @param account The initial account to grant all the tokens 62 | */ 63 | constructor(address account) public { 64 | balances[account] = uint96(totalSupply); 65 | emit Transfer(address(0), account, totalSupply); 66 | } 67 | 68 | /** 69 | * @notice Get the number of tokens `spender` is approved to spend on behalf of `account` 70 | * @param account The address of the account holding the funds 71 | * @param spender The address of the account spending the funds 72 | * @return The number of tokens approved 73 | */ 74 | function allowance(address account, address spender) external view returns (uint) { 75 | return allowances[account][spender]; 76 | } 77 | 78 | /** 79 | * @notice Approve `spender` to transfer up to `amount` from `src` 80 | * @dev This will overwrite the approval amount for `spender` 81 | * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 82 | * @param spender The address of the account which may transfer tokens 83 | * @param rawAmount The number of tokens that are approved (2^256-1 means infinite) 84 | * @return Whether or not the approval succeeded 85 | */ 86 | function approve(address spender, uint rawAmount) external returns (bool) { 87 | uint96 amount; 88 | if (rawAmount == uint(-1)) { 89 | amount = uint96(-1); 90 | } else { 91 | amount = safe96(rawAmount, "Can::approve: amount exceeds 96 bits"); 92 | } 93 | 94 | allowances[msg.sender][spender] = amount; 95 | 96 | emit Approval(msg.sender, spender, amount); 97 | return true; 98 | } 99 | 100 | /** 101 | * @notice Get the number of tokens held by the `account` 102 | * @param account The address of the account to get the balance of 103 | * @return The number of tokens held 104 | */ 105 | function balanceOf(address account) external view returns (uint) { 106 | return balances[account]; 107 | } 108 | 109 | /** 110 | * @notice Transfer `amount` tokens from `msg.sender` to `dst` 111 | * @param dst The address of the destination account 112 | * @param rawAmount The number of tokens to transfer 113 | * @return Whether or not the transfer succeeded 114 | */ 115 | function transfer(address dst, uint rawAmount) external returns (bool) { 116 | uint96 amount = safe96(rawAmount, "Can::transfer: amount exceeds 96 bits"); 117 | _transferTokens(msg.sender, dst, amount); 118 | return true; 119 | } 120 | 121 | /** 122 | * @notice Transfer `amount` tokens from `src` to `dst` 123 | * @param src The address of the source account 124 | * @param dst The address of the destination account 125 | * @param rawAmount The number of tokens to transfer 126 | * @return Whether or not the transfer succeeded 127 | */ 128 | function transferFrom(address src, address dst, uint rawAmount) external returns (bool) { 129 | address spender = msg.sender; 130 | uint96 spenderAllowance = allowances[src][spender]; 131 | uint96 amount = safe96(rawAmount, "Can::approve: amount exceeds 96 bits"); 132 | 133 | if (spender != src && spenderAllowance != uint96(-1)) { 134 | uint96 newAllowance = sub96(spenderAllowance, amount, "Can::transferFrom: transfer amount exceeds spender allowance"); 135 | allowances[src][spender] = newAllowance; 136 | 137 | emit Approval(src, spender, newAllowance); 138 | } 139 | 140 | _transferTokens(src, dst, amount); 141 | return true; 142 | } 143 | 144 | /** 145 | * @notice Delegate votes from `msg.sender` to `delegatee` 146 | * @param delegatee The address to delegate votes to 147 | */ 148 | function delegate(address delegatee) public { 149 | return _delegate(msg.sender, delegatee); 150 | } 151 | 152 | /** 153 | * @notice Delegates votes from signatory to `delegatee` 154 | * @param delegatee The address to delegate votes to 155 | * @param nonce The contract state required to match the signature 156 | * @param expiry The time at which to expire the signature 157 | * @param v The recovery byte of the signature 158 | * @param r Half of the ECDSA signature pair 159 | * @param s Half of the ECDSA signature pair 160 | */ 161 | function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public { 162 | bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); 163 | bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); 164 | bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 165 | address signatory = ecrecover(digest, v, r, s); 166 | require(signatory != address(0), "Can::delegateBySig: invalid signature"); 167 | require(nonce == nonces[signatory]++, "Can::delegateBySig: invalid nonce"); 168 | require(now <= expiry, "Can::delegateBySig: signature expired"); 169 | return _delegate(signatory, delegatee); 170 | } 171 | 172 | /** 173 | * @notice Gets the current votes balance for `account` 174 | * @param account The address to get votes balance 175 | * @return The number of current votes for `account` 176 | */ 177 | function getCurrentVotes(address account) external view returns (uint96) { 178 | uint32 nCheckpoints = numCheckpoints[account]; 179 | return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; 180 | } 181 | 182 | /** 183 | * @notice Determine the prior number of votes for an account as of a block number 184 | * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. 185 | * @param account The address of the account to check 186 | * @param blockNumber The block number to get the vote balance at 187 | * @return The number of votes the account had as of the given block 188 | */ 189 | function getPriorVotes(address account, uint blockNumber) public view returns (uint96) { 190 | require(blockNumber < block.number, "Can::getPriorVotes: not yet determined"); 191 | 192 | uint32 nCheckpoints = numCheckpoints[account]; 193 | if (nCheckpoints == 0) { 194 | return 0; 195 | } 196 | 197 | // First check most recent balance 198 | if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { 199 | return checkpoints[account][nCheckpoints - 1].votes; 200 | } 201 | 202 | // Next check implicit zero balance 203 | if (checkpoints[account][0].fromBlock > blockNumber) { 204 | return 0; 205 | } 206 | 207 | uint32 lower = 0; 208 | uint32 upper = nCheckpoints - 1; 209 | while (upper > lower) { 210 | uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow 211 | Checkpoint memory cp = checkpoints[account][center]; 212 | if (cp.fromBlock == blockNumber) { 213 | return cp.votes; 214 | } else if (cp.fromBlock < blockNumber) { 215 | lower = center; 216 | } else { 217 | upper = center - 1; 218 | } 219 | } 220 | return checkpoints[account][lower].votes; 221 | } 222 | 223 | function _delegate(address delegator, address delegatee) internal { 224 | address currentDelegate = delegates[delegator]; 225 | uint96 delegatorBalance = balances[delegator]; 226 | delegates[delegator] = delegatee; 227 | 228 | emit DelegateChanged(delegator, currentDelegate, delegatee); 229 | 230 | _moveDelegates(currentDelegate, delegatee, delegatorBalance); 231 | } 232 | 233 | function _transferTokens(address src, address dst, uint96 amount) internal { 234 | require(src != address(0), "Can::_transferTokens: cannot transfer from the zero address"); 235 | require(dst != address(0), "Can::_transferTokens: cannot transfer to the zero address"); 236 | 237 | balances[src] = sub96(balances[src], amount, "Can::_transferTokens: transfer amount exceeds balance"); 238 | balances[dst] = add96(balances[dst], amount, "Can::_transferTokens: transfer amount overflows"); 239 | emit Transfer(src, dst, amount); 240 | 241 | _moveDelegates(delegates[src], delegates[dst], amount); 242 | } 243 | 244 | function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal { 245 | if (srcRep != dstRep && amount > 0) { 246 | if (srcRep != address(0)) { 247 | uint32 srcRepNum = numCheckpoints[srcRep]; 248 | uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; 249 | uint96 srcRepNew = sub96(srcRepOld, amount, "Can::_moveVotes: vote amount underflows"); 250 | _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); 251 | } 252 | 253 | if (dstRep != address(0)) { 254 | uint32 dstRepNum = numCheckpoints[dstRep]; 255 | uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; 256 | uint96 dstRepNew = add96(dstRepOld, amount, "Can::_moveVotes: vote amount overflows"); 257 | _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); 258 | } 259 | } 260 | } 261 | 262 | function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal { 263 | uint32 blockNumber = safe32(block.number, "Can::_writeCheckpoint: block number exceeds 32 bits"); 264 | 265 | if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { 266 | checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; 267 | } else { 268 | checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); 269 | numCheckpoints[delegatee] = nCheckpoints + 1; 270 | } 271 | 272 | emit DelegateVotesChanged(delegatee, oldVotes, newVotes); 273 | } 274 | 275 | function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { 276 | require(n < 2**32, errorMessage); 277 | return uint32(n); 278 | } 279 | 280 | function safe96(uint n, string memory errorMessage) internal pure returns (uint96) { 281 | require(n < 2**96, errorMessage); 282 | return uint96(n); 283 | } 284 | 285 | function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { 286 | uint96 c = a + b; 287 | require(c >= a, errorMessage); 288 | return c; 289 | } 290 | 291 | function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { 292 | require(b <= a, errorMessage); 293 | return a - b; 294 | } 295 | 296 | function getChainId() internal pure returns (uint) { 297 | uint256 chainId; 298 | assembly { chainId := chainid() } 299 | return chainId; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /CarefulMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | /** 4 | * @title Careful Math 5 | * @author Channels 6 | * @notice Derived from OpenZeppelin's SafeMath library 7 | * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol 8 | */ 9 | contract CarefulMath { 10 | 11 | /** 12 | * @dev Possible error codes that we can return 13 | */ 14 | enum MathError { 15 | NO_ERROR, 16 | DIVISION_BY_ZERO, 17 | INTEGER_OVERFLOW, 18 | INTEGER_UNDERFLOW 19 | } 20 | 21 | /** 22 | * @dev Multiplies two numbers, returns an error on overflow. 23 | */ 24 | function mulUInt(uint a, uint b) internal pure returns (MathError, uint) { 25 | if (a == 0) { 26 | return (MathError.NO_ERROR, 0); 27 | } 28 | 29 | uint c = a * b; 30 | 31 | if (c / a != b) { 32 | return (MathError.INTEGER_OVERFLOW, 0); 33 | } else { 34 | return (MathError.NO_ERROR, c); 35 | } 36 | } 37 | 38 | /** 39 | * @dev Integer division of two numbers, truncating the quotient. 40 | */ 41 | function divUInt(uint a, uint b) internal pure returns (MathError, uint) { 42 | if (b == 0) { 43 | return (MathError.DIVISION_BY_ZERO, 0); 44 | } 45 | 46 | return (MathError.NO_ERROR, a / b); 47 | } 48 | 49 | /** 50 | * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). 51 | */ 52 | function subUInt(uint a, uint b) internal pure returns (MathError, uint) { 53 | if (b <= a) { 54 | return (MathError.NO_ERROR, a - b); 55 | } else { 56 | return (MathError.INTEGER_UNDERFLOW, 0); 57 | } 58 | } 59 | 60 | /** 61 | * @dev Adds two numbers, returns an error on overflow. 62 | */ 63 | function addUInt(uint a, uint b) internal pure returns (MathError, uint) { 64 | uint c = a + b; 65 | 66 | if (c >= a) { 67 | return (MathError.NO_ERROR, c); 68 | } else { 69 | return (MathError.INTEGER_OVERFLOW, 0); 70 | } 71 | } 72 | 73 | /** 74 | * @dev add a and b and then subtract c 75 | */ 76 | function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) { 77 | (MathError err0, uint sum) = addUInt(a, b); 78 | 79 | if (err0 != MathError.NO_ERROR) { 80 | return (err0, 0); 81 | } 82 | 83 | return subUInt(sum, c); 84 | } 85 | } -------------------------------------------------------------------------------- /ChannelsAnchoredOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.10; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./OpenOraclePriceData.sol"; 5 | import "./ChannelsOracleConfig.sol"; 6 | 7 | contract ChannelsAnchoredOracle is ChannelsOracleConfig { 8 | 9 | /// @notice The Open Oracle Price Data contract 10 | OpenOraclePriceData public immutable priceData; 11 | 12 | /// @notice The Open Oracle Reporter 13 | address public immutable reporter; 14 | 15 | /// @notice The highest ratio of the new price to the anchor price that will still trigger the price to be updated 16 | uint public immutable upperBoundAnchorRatio; 17 | 18 | /// @notice The lowest ratio of the new price to the anchor price that will still trigger the price to be updated 19 | uint public immutable lowerBoundAnchorRatio; 20 | 21 | /// @notice Official prices by symbol hash, default mul 1e6 22 | mapping(bytes32 => uint) public prices; 23 | 24 | /// @notice Oracle Args 25 | string _price; 26 | /// @notice Oracle Args 27 | ERC20 _token; 28 | /// @notice Oracle Args 29 | QuotedPrice _priceContract; 30 | /// @notice Oracle Args 31 | address _priceAddress; 32 | 33 | /// @notice The event emitted when new prices are posted but the stored price is not updated due to the anchor 34 | event PriceGuarded(string symbol, uint reporter, uint anchor); 35 | 36 | /// @notice The event emitted when the stored price is updated 37 | event PriceUpdated(string symbol, uint price); 38 | /** 39 | * @notice Construct a uniswap anchored view for a set of token configurations 40 | * @dev Note that to avoid immature TWAPs, the system must run for at least a single anchorPeriod before using. 41 | * @param reporter_ The reporter whose prices are to be used 42 | * @param anchorToleranceMantissa_ The percentage tolerance that the reporter may deviate from the uniswap anchor 43 | * @param configs The static token configurations which define what prices are supported and how 44 | */ 45 | constructor( 46 | address tokenAddress, 47 | address priceAddress, 48 | OpenOraclePriceData priceData_, 49 | address reporter_, 50 | uint anchorToleranceMantissa_, 51 | TokenConfig[] memory configs 52 | ) ChannelsOracleConfig(configs) public { 53 | 54 | _token = ERC20(tokenAddress); 55 | _priceContract = QuotedPrice(priceAddress); 56 | _priceAddress = priceAddress; 57 | 58 | priceData = priceData_; 59 | reporter = reporter_; 60 | 61 | // Allow the tolerance to be whatever the deployer chooses, but prevent under/overflow (and prices from being 0) 62 | upperBoundAnchorRatio = anchorToleranceMantissa_ > uint(- 1) - 100e16 ? uint(- 1) : 100e16 + anchorToleranceMantissa_; 63 | lowerBoundAnchorRatio = anchorToleranceMantissa_ < 100e16 ? 100e16 - anchorToleranceMantissa_ : 1; 64 | 65 | for (uint i = 0; i < configs.length; i++) { 66 | TokenConfig memory config = configs[i]; 67 | require(config.baseUnit > 0, "baseUnit must be greater than zero"); 68 | } 69 | } 70 | 71 | /// @notice symbol, example HT/USDT 72 | function queryPriceBySymbol(string memory symbol) public { 73 | if (_token.allowance(address(this), _priceAddress) < 200000000) { 74 | _token.approve(_priceAddress, 0); 75 | _token.approve(_priceAddress, 1000000000000000000); 76 | } 77 | _price = _priceContract.queryPrice(symbol); 78 | } 79 | 80 | function getPrice() public view returns (string memory){ 81 | return _price; 82 | } 83 | 84 | /** 85 | * @notice Get the official price for a symbol 86 | * @param symbol The symbol to fetch the price of 87 | * @return Price denominated in USD, with 6 decimals 88 | */ 89 | function price(string memory symbol) external view returns (uint) { 90 | TokenConfig memory config = getTokenConfigBySymbol(symbol); 91 | return priceInternal(config); 92 | } 93 | 94 | function priceInternal(TokenConfig memory config) internal view returns (uint) { 95 | if (config.priceSource == PriceSource.REPORTER) return prices[config.symbolHash]; 96 | if (config.priceSource == PriceSource.FIXED_USD) return config.fixedPrice; 97 | } 98 | 99 | /** 100 | * @notice Get the underlying price of a cToken 101 | * @dev Implements the PriceOracle interface for Channels v2. 102 | * @param cToken The cToken address for price retrieval 103 | * @return Price denominated in USD, with 18 decimals, for the given cToken address 104 | */ 105 | function getUnderlyingPrice(address cToken) external view returns (uint) { 106 | TokenConfig memory config = getTokenConfigByCToken(cToken); 107 | // Comptroller needs prices in the format: ${raw price} * 1e(36 - baseUnit) 108 | // Since the prices in this view have 6 decimals, we must scale them by 1e(36 - 6 - baseUnit) 109 | return mul(1e30, priceInternal(config)) / config.baseUnit; 110 | } 111 | 112 | /** 113 | * @notice Post open oracle reporter prices, and recalculate stored price by comparing to anchor 114 | * @dev We let anyone pay to post anything, but only prices from configured reporter will be stored in the view. 115 | * @param messages The messages to post to the oracle 116 | * @param signatures The signatures for the corresponding messages 117 | * @param symbols The symbols to compare to anchor for authoritative reading 118 | */ 119 | function postPrices(bytes[] calldata messages, bytes[] calldata signatures, string[] calldata symbols) external { 120 | require(messages.length == signatures.length, "messages and signatures must be 1:1"); 121 | require(msg.sender == reporter, "msg sender must be reporter "); 122 | 123 | // Save the prices 124 | for (uint i = 0; i < messages.length; i++) { 125 | priceData.put(messages[i], signatures[i]); 126 | } 127 | 128 | // Try to update the view storage 129 | for (uint i = 0; i < symbols.length; i++) { 130 | postPriceInternal(symbols[i]); 131 | } 132 | } 133 | 134 | function postPriceInternal(string memory symbol) internal { 135 | TokenConfig memory config = getTokenConfigBySymbol(symbol); 136 | require(config.priceSource == PriceSource.REPORTER, "only reporter prices get posted"); 137 | bytes32 symbolHash = keccak256(abi.encodePacked(symbol)); 138 | uint reporterPrice = priceData.getPrice(reporter, symbol); 139 | 140 | queryPriceBySymbol(config.symbolName); 141 | 142 | uint anchorPrice; 143 | anchorPrice = parseInt(_price, 6); 144 | 145 | if (isWithinAnchor(reporterPrice, anchorPrice)) { 146 | prices[symbolHash] = reporterPrice; 147 | emit PriceUpdated(symbol, reporterPrice); 148 | } else { 149 | emit PriceGuarded(symbol, reporterPrice, anchorPrice); 150 | revert("reporterPrice is not with in Anchor"); 151 | } 152 | } 153 | 154 | function isWithinAnchor(uint reporterPrice, uint anchorPrice) internal view returns (bool) { 155 | if (reporterPrice > 0) { 156 | uint anchorRatio = mul(anchorPrice, 100e16) / reporterPrice; 157 | return anchorRatio <= upperBoundAnchorRatio && anchorRatio >= lowerBoundAnchorRatio; 158 | } 159 | return false; 160 | } 161 | 162 | /// @dev Overflow proof multiplication 163 | function mul(uint a, uint b) internal pure returns (uint) { 164 | if (a == 0) return 0; 165 | uint c = a * b; 166 | require(c / a == b, "multiplication overflow"); 167 | return c; 168 | } 169 | 170 | function parseInt(string memory _a, uint _b) private pure returns (uint _parsedInt) { 171 | bytes memory bresult = bytes(_a); 172 | uint mint = 0; 173 | bool decimals = false; 174 | for (uint i = 0; i < bresult.length; i++) { 175 | if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) { 176 | if (decimals) { 177 | if (_b == 0) { 178 | break; 179 | } else { 180 | _b--; 181 | } 182 | } 183 | mint *= 10; 184 | mint += uint(uint8(bresult[i])) - 48; 185 | } else if (uint(uint8(bresult[i])) == 46) { 186 | decimals = true; 187 | } 188 | } 189 | if (_b > 0) { 190 | mint *= 10 ** _b; 191 | } 192 | return mint; 193 | } 194 | } 195 | 196 | interface QuotedPrice { 197 | function queryPrice(string calldata symbol) external returns (string memory price); 198 | } 199 | 200 | interface ERC20 { 201 | function approve(address spender, uint256 amount) external returns (bool); 202 | 203 | function allowance(address _owner, address _spender) external returns (uint256 remaining); 204 | } 205 | 206 | -------------------------------------------------------------------------------- /ChannelsOracleConfig.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.6.10; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface CErc20 { 6 | function underlying() external view returns (address); 7 | } 8 | 9 | contract ChannelsOracleConfig { 10 | /// @dev Describe how to interpret the fixedPrice in the TokenConfig. 11 | enum PriceSource { 12 | FIXED_ETH, /// implies the fixedPrice is a constant multiple of the ETH price (which varies) 13 | FIXED_USD, /// implies the fixedPrice is a constant multiple of the USD price (which is 1) 14 | REPORTER /// implies the price is set by the reporter 15 | } 16 | 17 | /// @dev Describe how the USD price should be determined for an asset. 18 | /// There should be 1 TokenConfig object for each supported asset, passed in the constructor. 19 | struct TokenConfig { 20 | address cToken; 21 | address underlying; 22 | bytes32 symbolHash; 23 | uint256 baseUnit; 24 | PriceSource priceSource; 25 | uint256 fixedPrice; 26 | string symbolName; 27 | } 28 | 29 | /// @notice The max number of tokens this contract is hardcoded to support 30 | /// @dev Do not change this variable without updating all the fields throughout the contract. 31 | uint public constant maxTokens = 20; 32 | 33 | /// @notice The number of tokens this contract actually supports 34 | uint public immutable numTokens; 35 | 36 | address internal immutable cToken00; 37 | address internal immutable cToken01; 38 | address internal immutable cToken02; 39 | address internal immutable cToken03; 40 | address internal immutable cToken04; 41 | address internal immutable cToken05; 42 | address internal immutable cToken06; 43 | address internal immutable cToken07; 44 | address internal immutable cToken08; 45 | address internal immutable cToken09; 46 | address internal immutable cToken10; 47 | address internal immutable cToken11; 48 | address internal immutable cToken12; 49 | address internal immutable cToken13; 50 | address internal immutable cToken14; 51 | address internal immutable cToken15; 52 | address internal immutable cToken16; 53 | address internal immutable cToken17; 54 | address internal immutable cToken18; 55 | address internal immutable cToken19; 56 | 57 | address internal immutable underlying00; 58 | address internal immutable underlying01; 59 | address internal immutable underlying02; 60 | address internal immutable underlying03; 61 | address internal immutable underlying04; 62 | address internal immutable underlying05; 63 | address internal immutable underlying06; 64 | address internal immutable underlying07; 65 | address internal immutable underlying08; 66 | address internal immutable underlying09; 67 | address internal immutable underlying10; 68 | address internal immutable underlying11; 69 | address internal immutable underlying12; 70 | address internal immutable underlying13; 71 | address internal immutable underlying14; 72 | address internal immutable underlying15; 73 | address internal immutable underlying16; 74 | address internal immutable underlying17; 75 | address internal immutable underlying18; 76 | address internal immutable underlying19; 77 | 78 | bytes32 internal immutable symbolHash00; 79 | bytes32 internal immutable symbolHash01; 80 | bytes32 internal immutable symbolHash02; 81 | bytes32 internal immutable symbolHash03; 82 | bytes32 internal immutable symbolHash04; 83 | bytes32 internal immutable symbolHash05; 84 | bytes32 internal immutable symbolHash06; 85 | bytes32 internal immutable symbolHash07; 86 | bytes32 internal immutable symbolHash08; 87 | bytes32 internal immutable symbolHash09; 88 | bytes32 internal immutable symbolHash10; 89 | bytes32 internal immutable symbolHash11; 90 | bytes32 internal immutable symbolHash12; 91 | bytes32 internal immutable symbolHash13; 92 | bytes32 internal immutable symbolHash14; 93 | bytes32 internal immutable symbolHash15; 94 | bytes32 internal immutable symbolHash16; 95 | bytes32 internal immutable symbolHash17; 96 | bytes32 internal immutable symbolHash18; 97 | bytes32 internal immutable symbolHash19; 98 | 99 | uint256 internal immutable baseUnit00; 100 | uint256 internal immutable baseUnit01; 101 | uint256 internal immutable baseUnit02; 102 | uint256 internal immutable baseUnit03; 103 | uint256 internal immutable baseUnit04; 104 | uint256 internal immutable baseUnit05; 105 | uint256 internal immutable baseUnit06; 106 | uint256 internal immutable baseUnit07; 107 | uint256 internal immutable baseUnit08; 108 | uint256 internal immutable baseUnit09; 109 | uint256 internal immutable baseUnit10; 110 | uint256 internal immutable baseUnit11; 111 | uint256 internal immutable baseUnit12; 112 | uint256 internal immutable baseUnit13; 113 | uint256 internal immutable baseUnit14; 114 | uint256 internal immutable baseUnit15; 115 | uint256 internal immutable baseUnit16; 116 | uint256 internal immutable baseUnit17; 117 | uint256 internal immutable baseUnit18; 118 | uint256 internal immutable baseUnit19; 119 | 120 | PriceSource internal immutable priceSource00; 121 | PriceSource internal immutable priceSource01; 122 | PriceSource internal immutable priceSource02; 123 | PriceSource internal immutable priceSource03; 124 | PriceSource internal immutable priceSource04; 125 | PriceSource internal immutable priceSource05; 126 | PriceSource internal immutable priceSource06; 127 | PriceSource internal immutable priceSource07; 128 | PriceSource internal immutable priceSource08; 129 | PriceSource internal immutable priceSource09; 130 | PriceSource internal immutable priceSource10; 131 | PriceSource internal immutable priceSource11; 132 | PriceSource internal immutable priceSource12; 133 | PriceSource internal immutable priceSource13; 134 | PriceSource internal immutable priceSource14; 135 | PriceSource internal immutable priceSource15; 136 | PriceSource internal immutable priceSource16; 137 | PriceSource internal immutable priceSource17; 138 | PriceSource internal immutable priceSource18; 139 | PriceSource internal immutable priceSource19; 140 | 141 | uint256 internal immutable fixedPrice00; 142 | uint256 internal immutable fixedPrice01; 143 | uint256 internal immutable fixedPrice02; 144 | uint256 internal immutable fixedPrice03; 145 | uint256 internal immutable fixedPrice04; 146 | uint256 internal immutable fixedPrice05; 147 | uint256 internal immutable fixedPrice06; 148 | uint256 internal immutable fixedPrice07; 149 | uint256 internal immutable fixedPrice08; 150 | uint256 internal immutable fixedPrice09; 151 | uint256 internal immutable fixedPrice10; 152 | uint256 internal immutable fixedPrice11; 153 | uint256 internal immutable fixedPrice12; 154 | uint256 internal immutable fixedPrice13; 155 | uint256 internal immutable fixedPrice14; 156 | uint256 internal immutable fixedPrice15; 157 | uint256 internal immutable fixedPrice16; 158 | uint256 internal immutable fixedPrice17; 159 | uint256 internal immutable fixedPrice18; 160 | uint256 internal immutable fixedPrice19; 161 | 162 | string internal symbolName00; 163 | string internal symbolName01; 164 | string internal symbolName02; 165 | string internal symbolName03; 166 | string internal symbolName04; 167 | string internal symbolName05; 168 | string internal symbolName06; 169 | string internal symbolName07; 170 | string internal symbolName08; 171 | string internal symbolName09; 172 | string internal symbolName10; 173 | string internal symbolName11; 174 | string internal symbolName12; 175 | string internal symbolName13; 176 | string internal symbolName14; 177 | string internal symbolName15; 178 | string internal symbolName16; 179 | string internal symbolName17; 180 | string internal symbolName18; 181 | string internal symbolName19; 182 | 183 | /** 184 | * @notice Construct an immutable store of configs into the contract data 185 | * @param configs The configs for the supported assets 186 | */ 187 | constructor(TokenConfig[] memory configs) public { 188 | require(configs.length <= maxTokens, "too many configs"); 189 | numTokens = configs.length; 190 | 191 | cToken00 = get(configs, 0).cToken; 192 | cToken01 = get(configs, 1).cToken; 193 | cToken02 = get(configs, 2).cToken; 194 | cToken03 = get(configs, 3).cToken; 195 | cToken04 = get(configs, 4).cToken; 196 | cToken05 = get(configs, 5).cToken; 197 | cToken06 = get(configs, 6).cToken; 198 | cToken07 = get(configs, 7).cToken; 199 | cToken08 = get(configs, 8).cToken; 200 | cToken09 = get(configs, 9).cToken; 201 | cToken10 = get(configs, 10).cToken; 202 | cToken11 = get(configs, 11).cToken; 203 | cToken12 = get(configs, 12).cToken; 204 | cToken13 = get(configs, 13).cToken; 205 | cToken14 = get(configs, 14).cToken; 206 | cToken15 = get(configs, 15).cToken; 207 | cToken16 = get(configs, 16).cToken; 208 | cToken17 = get(configs, 17).cToken; 209 | cToken18 = get(configs, 18).cToken; 210 | cToken19 = get(configs, 19).cToken; 211 | 212 | underlying00 = get(configs, 0).underlying; 213 | underlying01 = get(configs, 1).underlying; 214 | underlying02 = get(configs, 2).underlying; 215 | underlying03 = get(configs, 3).underlying; 216 | underlying04 = get(configs, 4).underlying; 217 | underlying05 = get(configs, 5).underlying; 218 | underlying06 = get(configs, 6).underlying; 219 | underlying07 = get(configs, 7).underlying; 220 | underlying08 = get(configs, 8).underlying; 221 | underlying09 = get(configs, 9).underlying; 222 | underlying10 = get(configs, 10).underlying; 223 | underlying11 = get(configs, 11).underlying; 224 | underlying12 = get(configs, 12).underlying; 225 | underlying13 = get(configs, 13).underlying; 226 | underlying14 = get(configs, 14).underlying; 227 | underlying15 = get(configs, 15).underlying; 228 | underlying16 = get(configs, 16).underlying; 229 | underlying17 = get(configs, 17).underlying; 230 | underlying18 = get(configs, 18).underlying; 231 | underlying19 = get(configs, 19).underlying; 232 | 233 | symbolHash00 = get(configs, 0).symbolHash; 234 | symbolHash01 = get(configs, 1).symbolHash; 235 | symbolHash02 = get(configs, 2).symbolHash; 236 | symbolHash03 = get(configs, 3).symbolHash; 237 | symbolHash04 = get(configs, 4).symbolHash; 238 | symbolHash05 = get(configs, 5).symbolHash; 239 | symbolHash06 = get(configs, 6).symbolHash; 240 | symbolHash07 = get(configs, 7).symbolHash; 241 | symbolHash08 = get(configs, 8).symbolHash; 242 | symbolHash09 = get(configs, 9).symbolHash; 243 | symbolHash10 = get(configs, 10).symbolHash; 244 | symbolHash11 = get(configs, 11).symbolHash; 245 | symbolHash12 = get(configs, 12).symbolHash; 246 | symbolHash13 = get(configs, 13).symbolHash; 247 | symbolHash14 = get(configs, 14).symbolHash; 248 | symbolHash15 = get(configs, 15).symbolHash; 249 | symbolHash16 = get(configs, 16).symbolHash; 250 | symbolHash17 = get(configs, 17).symbolHash; 251 | symbolHash18 = get(configs, 18).symbolHash; 252 | symbolHash19 = get(configs, 19).symbolHash; 253 | 254 | baseUnit00 = get(configs, 0).baseUnit; 255 | baseUnit01 = get(configs, 1).baseUnit; 256 | baseUnit02 = get(configs, 2).baseUnit; 257 | baseUnit03 = get(configs, 3).baseUnit; 258 | baseUnit04 = get(configs, 4).baseUnit; 259 | baseUnit05 = get(configs, 5).baseUnit; 260 | baseUnit06 = get(configs, 6).baseUnit; 261 | baseUnit07 = get(configs, 7).baseUnit; 262 | baseUnit08 = get(configs, 8).baseUnit; 263 | baseUnit09 = get(configs, 9).baseUnit; 264 | baseUnit10 = get(configs, 10).baseUnit; 265 | baseUnit11 = get(configs, 11).baseUnit; 266 | baseUnit12 = get(configs, 12).baseUnit; 267 | baseUnit13 = get(configs, 13).baseUnit; 268 | baseUnit14 = get(configs, 14).baseUnit; 269 | baseUnit15 = get(configs, 15).baseUnit; 270 | baseUnit16 = get(configs, 16).baseUnit; 271 | baseUnit17 = get(configs, 17).baseUnit; 272 | baseUnit18 = get(configs, 18).baseUnit; 273 | baseUnit19 = get(configs, 19).baseUnit; 274 | 275 | priceSource00 = get(configs, 0).priceSource; 276 | priceSource01 = get(configs, 1).priceSource; 277 | priceSource02 = get(configs, 2).priceSource; 278 | priceSource03 = get(configs, 3).priceSource; 279 | priceSource04 = get(configs, 4).priceSource; 280 | priceSource05 = get(configs, 5).priceSource; 281 | priceSource06 = get(configs, 6).priceSource; 282 | priceSource07 = get(configs, 7).priceSource; 283 | priceSource08 = get(configs, 8).priceSource; 284 | priceSource09 = get(configs, 9).priceSource; 285 | priceSource10 = get(configs, 10).priceSource; 286 | priceSource11 = get(configs, 11).priceSource; 287 | priceSource12 = get(configs, 12).priceSource; 288 | priceSource13 = get(configs, 13).priceSource; 289 | priceSource14 = get(configs, 14).priceSource; 290 | priceSource15 = get(configs, 15).priceSource; 291 | priceSource16 = get(configs, 16).priceSource; 292 | priceSource17 = get(configs, 17).priceSource; 293 | priceSource18 = get(configs, 18).priceSource; 294 | priceSource19 = get(configs, 19).priceSource; 295 | 296 | 297 | fixedPrice00 = get(configs, 0).fixedPrice; 298 | fixedPrice01 = get(configs, 1).fixedPrice; 299 | fixedPrice02 = get(configs, 2).fixedPrice; 300 | fixedPrice03 = get(configs, 3).fixedPrice; 301 | fixedPrice04 = get(configs, 4).fixedPrice; 302 | fixedPrice05 = get(configs, 5).fixedPrice; 303 | fixedPrice06 = get(configs, 6).fixedPrice; 304 | fixedPrice07 = get(configs, 7).fixedPrice; 305 | fixedPrice08 = get(configs, 8).fixedPrice; 306 | fixedPrice09 = get(configs, 9).fixedPrice; 307 | fixedPrice10 = get(configs, 10).fixedPrice; 308 | fixedPrice11 = get(configs, 11).fixedPrice; 309 | fixedPrice12 = get(configs, 12).fixedPrice; 310 | fixedPrice13 = get(configs, 13).fixedPrice; 311 | fixedPrice14 = get(configs, 14).fixedPrice; 312 | fixedPrice15 = get(configs, 15).fixedPrice; 313 | fixedPrice16 = get(configs, 16).fixedPrice; 314 | fixedPrice17 = get(configs, 17).fixedPrice; 315 | fixedPrice18 = get(configs, 18).fixedPrice; 316 | fixedPrice19 = get(configs, 19).fixedPrice; 317 | 318 | 319 | symbolName00 = get(configs, 0).symbolName; 320 | symbolName01 = get(configs, 1).symbolName; 321 | symbolName02 = get(configs, 2).symbolName; 322 | symbolName03 = get(configs, 3).symbolName; 323 | symbolName04 = get(configs, 4).symbolName; 324 | symbolName05 = get(configs, 5).symbolName; 325 | symbolName06 = get(configs, 6).symbolName; 326 | symbolName07 = get(configs, 7).symbolName; 327 | symbolName08 = get(configs, 8).symbolName; 328 | symbolName09 = get(configs, 9).symbolName; 329 | symbolName10 = get(configs, 10).symbolName; 330 | symbolName11 = get(configs, 11).symbolName; 331 | symbolName12 = get(configs, 12).symbolName; 332 | symbolName13 = get(configs, 13).symbolName; 333 | symbolName14 = get(configs, 14).symbolName; 334 | symbolName15 = get(configs, 15).symbolName; 335 | symbolName16 = get(configs, 16).symbolName; 336 | symbolName17 = get(configs, 17).symbolName; 337 | symbolName18 = get(configs, 18).symbolName; 338 | symbolName19 = get(configs, 19).symbolName; 339 | } 340 | 341 | function get(TokenConfig[] memory configs, uint i) internal pure returns (TokenConfig memory) { 342 | if (i < configs.length) return configs[i]; 343 | return TokenConfig({ 344 | cToken: address(0), 345 | underlying: address(0), 346 | symbolHash: bytes32(0), 347 | baseUnit: uint256(0), 348 | priceSource: PriceSource(0), 349 | fixedPrice: uint256(0), 350 | symbolName: "" 351 | }); 352 | } 353 | 354 | function getCTokenIndex(address cToken) internal view returns (uint) { 355 | if (cToken == cToken00) return 0; 356 | if (cToken == cToken01) return 1; 357 | if (cToken == cToken02) return 2; 358 | if (cToken == cToken03) return 3; 359 | if (cToken == cToken04) return 4; 360 | if (cToken == cToken05) return 5; 361 | if (cToken == cToken06) return 6; 362 | if (cToken == cToken07) return 7; 363 | if (cToken == cToken08) return 8; 364 | if (cToken == cToken09) return 9; 365 | if (cToken == cToken10) return 10; 366 | if (cToken == cToken11) return 11; 367 | if (cToken == cToken12) return 12; 368 | if (cToken == cToken13) return 13; 369 | if (cToken == cToken14) return 14; 370 | if (cToken == cToken15) return 15; 371 | if (cToken == cToken16) return 16; 372 | if (cToken == cToken17) return 17; 373 | if (cToken == cToken18) return 18; 374 | if (cToken == cToken19) return 19; 375 | 376 | 377 | return uint(-1); 378 | } 379 | 380 | function getUnderlyingIndex(address underlying) internal view returns (uint) { 381 | if (underlying == underlying00) return 0; 382 | if (underlying == underlying01) return 1; 383 | if (underlying == underlying02) return 2; 384 | if (underlying == underlying03) return 3; 385 | if (underlying == underlying04) return 4; 386 | if (underlying == underlying05) return 5; 387 | if (underlying == underlying06) return 6; 388 | if (underlying == underlying07) return 7; 389 | if (underlying == underlying08) return 8; 390 | if (underlying == underlying09) return 9; 391 | if (underlying == underlying10) return 10; 392 | if (underlying == underlying11) return 11; 393 | if (underlying == underlying12) return 12; 394 | if (underlying == underlying13) return 13; 395 | if (underlying == underlying14) return 14; 396 | if (underlying == underlying15) return 15; 397 | if (underlying == underlying16) return 16; 398 | if (underlying == underlying17) return 17; 399 | if (underlying == underlying18) return 18; 400 | if (underlying == underlying19) return 19; 401 | 402 | 403 | return uint(-1); 404 | } 405 | 406 | function getSymbolHashIndex(bytes32 symbolHash) internal view returns (uint) { 407 | if (symbolHash == symbolHash00) return 0; 408 | if (symbolHash == symbolHash01) return 1; 409 | if (symbolHash == symbolHash02) return 2; 410 | if (symbolHash == symbolHash03) return 3; 411 | if (symbolHash == symbolHash04) return 4; 412 | if (symbolHash == symbolHash05) return 5; 413 | if (symbolHash == symbolHash06) return 6; 414 | if (symbolHash == symbolHash07) return 7; 415 | if (symbolHash == symbolHash08) return 8; 416 | if (symbolHash == symbolHash09) return 9; 417 | if (symbolHash == symbolHash10) return 10; 418 | if (symbolHash == symbolHash11) return 11; 419 | if (symbolHash == symbolHash12) return 12; 420 | if (symbolHash == symbolHash13) return 13; 421 | if (symbolHash == symbolHash14) return 14; 422 | if (symbolHash == symbolHash15) return 15; 423 | if (symbolHash == symbolHash16) return 16; 424 | if (symbolHash == symbolHash17) return 17; 425 | if (symbolHash == symbolHash18) return 18; 426 | if (symbolHash == symbolHash19) return 19; 427 | 428 | 429 | return uint(-1); 430 | } 431 | 432 | /** 433 | * @notice Get the i-th config, according to the order they were passed in originally 434 | * @param i The index of the config to get 435 | * @return The config object 436 | */ 437 | function getTokenConfig(uint i) public view returns (TokenConfig memory) { 438 | require(i < numTokens, "token config not found"); 439 | 440 | if (i == 0) return TokenConfig({cToken: cToken00, underlying: underlying00, symbolHash: symbolHash00, baseUnit: baseUnit00, priceSource: priceSource00, fixedPrice: fixedPrice00, symbolName: symbolName00}); 441 | if (i == 1) return TokenConfig({cToken: cToken01, underlying: underlying01, symbolHash: symbolHash01, baseUnit: baseUnit01, priceSource: priceSource01, fixedPrice: fixedPrice01, symbolName: symbolName01}); 442 | if (i == 2) return TokenConfig({cToken: cToken02, underlying: underlying02, symbolHash: symbolHash02, baseUnit: baseUnit02, priceSource: priceSource02, fixedPrice: fixedPrice02, symbolName: symbolName02}); 443 | if (i == 3) return TokenConfig({cToken: cToken03, underlying: underlying03, symbolHash: symbolHash03, baseUnit: baseUnit03, priceSource: priceSource03, fixedPrice: fixedPrice03, symbolName: symbolName03}); 444 | if (i == 4) return TokenConfig({cToken: cToken04, underlying: underlying04, symbolHash: symbolHash04, baseUnit: baseUnit04, priceSource: priceSource04, fixedPrice: fixedPrice04, symbolName: symbolName04}); 445 | if (i == 5) return TokenConfig({cToken: cToken05, underlying: underlying05, symbolHash: symbolHash05, baseUnit: baseUnit05, priceSource: priceSource05, fixedPrice: fixedPrice05, symbolName: symbolName05}); 446 | if (i == 6) return TokenConfig({cToken: cToken06, underlying: underlying06, symbolHash: symbolHash06, baseUnit: baseUnit06, priceSource: priceSource06, fixedPrice: fixedPrice06, symbolName: symbolName06}); 447 | if (i == 7) return TokenConfig({cToken: cToken07, underlying: underlying07, symbolHash: symbolHash07, baseUnit: baseUnit07, priceSource: priceSource07, fixedPrice: fixedPrice07, symbolName: symbolName07}); 448 | if (i == 8) return TokenConfig({cToken: cToken08, underlying: underlying08, symbolHash: symbolHash08, baseUnit: baseUnit08, priceSource: priceSource08, fixedPrice: fixedPrice08, symbolName: symbolName08}); 449 | if (i == 9) return TokenConfig({cToken: cToken09, underlying: underlying09, symbolHash: symbolHash09, baseUnit: baseUnit09, priceSource: priceSource09, fixedPrice: fixedPrice09, symbolName: symbolName09}); 450 | 451 | if (i == 10) return TokenConfig({cToken: cToken10, underlying: underlying10, symbolHash: symbolHash10, baseUnit: baseUnit10, priceSource: priceSource10, fixedPrice: fixedPrice10, symbolName: symbolName10}); 452 | if (i == 11) return TokenConfig({cToken: cToken11, underlying: underlying11, symbolHash: symbolHash11, baseUnit: baseUnit11, priceSource: priceSource11, fixedPrice: fixedPrice11, symbolName: symbolName11}); 453 | if (i == 12) return TokenConfig({cToken: cToken12, underlying: underlying12, symbolHash: symbolHash12, baseUnit: baseUnit12, priceSource: priceSource12, fixedPrice: fixedPrice12, symbolName: symbolName12}); 454 | if (i == 13) return TokenConfig({cToken: cToken13, underlying: underlying13, symbolHash: symbolHash13, baseUnit: baseUnit13, priceSource: priceSource13, fixedPrice: fixedPrice13, symbolName: symbolName13}); 455 | if (i == 14) return TokenConfig({cToken: cToken14, underlying: underlying14, symbolHash: symbolHash14, baseUnit: baseUnit14, priceSource: priceSource14, fixedPrice: fixedPrice14, symbolName: symbolName14}); 456 | if (i == 15) return TokenConfig({cToken: cToken15, underlying: underlying15, symbolHash: symbolHash15, baseUnit: baseUnit15, priceSource: priceSource15, fixedPrice: fixedPrice15, symbolName: symbolName15}); 457 | if (i == 16) return TokenConfig({cToken: cToken16, underlying: underlying16, symbolHash: symbolHash16, baseUnit: baseUnit16, priceSource: priceSource16, fixedPrice: fixedPrice16, symbolName: symbolName16}); 458 | if (i == 17) return TokenConfig({cToken: cToken17, underlying: underlying17, symbolHash: symbolHash17, baseUnit: baseUnit17, priceSource: priceSource17, fixedPrice: fixedPrice17, symbolName: symbolName17}); 459 | if (i == 18) return TokenConfig({cToken: cToken18, underlying: underlying18, symbolHash: symbolHash18, baseUnit: baseUnit18, priceSource: priceSource18, fixedPrice: fixedPrice18, symbolName: symbolName18}); 460 | if (i == 19) return TokenConfig({cToken: cToken19, underlying: underlying19, symbolHash: symbolHash19, baseUnit: baseUnit19, priceSource: priceSource19, fixedPrice: fixedPrice19, symbolName: symbolName19}); 461 | 462 | } 463 | 464 | /** 465 | * @notice Get the config for symbol 466 | * @param symbol The symbol of the config to get 467 | * @return The config object 468 | */ 469 | function getTokenConfigBySymbol(string memory symbol) public view returns (TokenConfig memory) { 470 | return getTokenConfigBySymbolHash(keccak256(abi.encodePacked(symbol))); 471 | } 472 | 473 | /** 474 | * @notice Get the config for the symbolHash 475 | * @param symbolHash The keccack256 of the symbol of the config to get 476 | * @return The config object 477 | */ 478 | function getTokenConfigBySymbolHash(bytes32 symbolHash) public view returns (TokenConfig memory) { 479 | uint index = getSymbolHashIndex(symbolHash); 480 | if (index != uint(-1)) { 481 | return getTokenConfig(index); 482 | } 483 | 484 | revert("token config not found"); 485 | } 486 | 487 | /** 488 | * @notice Get the config for the cToken 489 | * @dev If a config for the cToken is not found, falls back to searching for the underlying. 490 | * @param cToken The address of the cToken of the config to get 491 | * @return The config object 492 | */ 493 | function getTokenConfigByCToken(address cToken) public view returns (TokenConfig memory) { 494 | uint index = getCTokenIndex(cToken); 495 | if (index != uint(-1)) { 496 | return getTokenConfig(index); 497 | } 498 | 499 | return getTokenConfigByUnderlying(CErc20(cToken).underlying()); 500 | } 501 | 502 | /** 503 | * @notice Get the config for an underlying asset 504 | * @param underlying The address of the underlying asset of the config to get 505 | * @return The config object 506 | */ 507 | function getTokenConfigByUnderlying(address underlying) public view returns (TokenConfig memory) { 508 | uint index = getUnderlyingIndex(underlying); 509 | if (index != uint(-1)) { 510 | return getTokenConfig(index); 511 | } 512 | 513 | revert("token config not found"); 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /Comptroller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CToken.sol"; 4 | import "./ErrorReporter.sol"; 5 | import "./Exponential.sol"; 6 | import "./PriceOracle.sol"; 7 | import "./ComptrollerInterface.sol"; 8 | import "./ComptrollerStorage.sol"; 9 | import "./Unitroller.sol"; 10 | import "./Can.sol"; 11 | 12 | /** 13 | * @title Channels's Comptroller Contract 14 | * @author Channels 15 | */ 16 | contract Comptroller is ComptrollerV3Storage, ComptrollerInterface, ComptrollerErrorReporter, Exponential { 17 | /// @notice Emitted when an admin supports a market 18 | event MarketListed(CToken cToken); 19 | 20 | /// @notice Emitted when an account enters a market 21 | event MarketEntered(CToken cToken, address account); 22 | 23 | /// @notice Emitted when an account exits a market 24 | event MarketExited(CToken cToken, address account); 25 | 26 | /// @notice Emitted when close factor is changed by admin 27 | event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); 28 | 29 | /// @notice Emitted when a collateral factor is changed by admin 30 | event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); 31 | 32 | /// @notice Emitted when liquidation incentive is changed by admin 33 | event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); 34 | 35 | /// @notice Emitted when maxAssets is changed by admin 36 | event NewMaxAssets(uint oldMaxAssets, uint newMaxAssets); 37 | 38 | /// @notice Emitted when price oracle is changed 39 | event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); 40 | 41 | /// @notice Emitted when pause guardian is changed 42 | event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); 43 | 44 | /// @notice Emitted when an action is paused globally 45 | event ActionPaused(string action, bool pauseState); 46 | 47 | /// @notice Emitted when an action is paused on a market 48 | event ActionPaused(CToken cToken, string action, bool pauseState); 49 | 50 | /// @notice Emitted when market caned status is changed 51 | event MarketCaned(CToken cToken, bool isCaned); 52 | 53 | /// @notice Emitted when Channels rate is changed 54 | event NewCanRate(uint oldCanRate, uint newCanRate); 55 | 56 | /// @notice Emitted when a new Channels speed is calculated for a market 57 | event CanSpeedUpdated(CToken indexed cToken, uint newSpeed); 58 | 59 | /// @notice Emitted when Channels is distributed to a supplier 60 | event DistributedSupplierCan(CToken indexed cToken, address indexed supplier, uint canDelta, uint canSupplyIndex); 61 | 62 | /// @notice Emitted when Channels is distributed to a borrower 63 | event DistributedBorrowerCan(CToken indexed cToken, address indexed borrower, uint canDelta, uint canBorrowIndex); 64 | 65 | /// @notice The threshold above which the flywheel transfers Channels, in wei 66 | uint public constant canClaimThreshold = 0.001e18; 67 | 68 | /// @notice The initial Channels index for a market 69 | uint224 public constant canInitialIndex = 1e36; 70 | 71 | // closeFactorMantissa must be strictly greater than this value 72 | uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 73 | 74 | // closeFactorMantissa must not exceed this value 75 | uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 76 | 77 | // No collateralFactorMantissa may exceed this value 78 | uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 79 | 80 | // liquidationIncentiveMantissa must be no less than this value 81 | uint internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0 82 | 83 | // liquidationIncentiveMantissa must be no greater than this value 84 | uint internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5 85 | 86 | constructor() public { 87 | admin = msg.sender; 88 | } 89 | 90 | /*** Assets You Are In ***/ 91 | 92 | /** 93 | * @notice Returns the assets an account has entered 94 | * @param account The address of the account to pull assets for 95 | * @return A dynamic list with the assets the account has entered 96 | */ 97 | function getAssetsIn(address account) external view returns (CToken[] memory) { 98 | CToken[] memory assetsIn = accountAssets[account]; 99 | 100 | return assetsIn; 101 | } 102 | 103 | /** 104 | * @notice Returns whether the given account is entered in the given asset 105 | * @param account The address of the account to check 106 | * @param cToken The cToken to check 107 | * @return True if the account is in the asset, otherwise false. 108 | */ 109 | function checkMembership(address account, CToken cToken) external view returns (bool) { 110 | return markets[address(cToken)].accountMembership[account]; 111 | } 112 | 113 | /** 114 | * @notice Add assets to be included in account liquidity calculation 115 | * @param cTokens The list of addresses of the cToken markets to be enabled 116 | * @return Success indicator for whether each corresponding market was entered 117 | */ 118 | function enterMarkets(address[] memory cTokens) public returns (uint[] memory) { 119 | uint len = cTokens.length; 120 | 121 | uint[] memory results = new uint[](len); 122 | for (uint i = 0; i < len; i++) { 123 | CToken cToken = CToken(cTokens[i]); 124 | 125 | results[i] = uint(addToMarketInternal(cToken, msg.sender)); 126 | } 127 | 128 | return results; 129 | } 130 | 131 | /** 132 | * @notice Add the market to the borrower's "assets in" for liquidity calculations 133 | * @param cToken The market to enter 134 | * @param borrower The address of the account to modify 135 | * @return Success indicator for whether the market was entered 136 | */ 137 | function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) { 138 | Market storage marketToJoin = markets[address(cToken)]; 139 | 140 | if (!marketToJoin.isListed) { 141 | // market is not listed, cannot join 142 | return Error.MARKET_NOT_LISTED; 143 | } 144 | 145 | if (marketToJoin.accountMembership[borrower] == true) { 146 | // already joined 147 | return Error.NO_ERROR; 148 | } 149 | 150 | if (accountAssets[borrower].length >= maxAssets) { 151 | // no space, cannot join 152 | return Error.TOO_MANY_ASSETS; 153 | } 154 | 155 | // survived the gauntlet, add to list 156 | // NOTE: we store these somewhat redundantly as a significant optimization 157 | // this avoids having to iterate through the list for the most common use cases 158 | // that is, only when we need to perform liquidity checks 159 | // and not whenever we want to check if an account is in a particular market 160 | marketToJoin.accountMembership[borrower] = true; 161 | accountAssets[borrower].push(cToken); 162 | 163 | emit MarketEntered(cToken, borrower); 164 | 165 | return Error.NO_ERROR; 166 | } 167 | 168 | /** 169 | * @notice Removes asset from sender's account liquidity calculation 170 | * @dev Sender must not have an outstanding borrow balance in the asset, 171 | * or be providing necessary collateral for an outstanding borrow. 172 | * @param cTokenAddress The address of the asset to be removed 173 | * @return Whether or not the account successfully exited the market 174 | */ 175 | function exitMarket(address cTokenAddress) external returns (uint) { 176 | CToken cToken = CToken(cTokenAddress); 177 | /* Get sender tokensHeld and amountOwed underlying from the cToken */ 178 | (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender); 179 | require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code 180 | 181 | /* Fail if the sender has a borrow balance */ 182 | if (amountOwed != 0) { 183 | return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED); 184 | } 185 | 186 | /* Fail if the sender is not permitted to redeem all of their tokens */ 187 | uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); 188 | if (allowed != 0) { 189 | return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); 190 | } 191 | 192 | Market storage marketToExit = markets[address(cToken)]; 193 | 194 | /* Return true if the sender is not already ‘in’ the market */ 195 | if (!marketToExit.accountMembership[msg.sender]) { 196 | return uint(Error.NO_ERROR); 197 | } 198 | 199 | /* Set cToken account membership to false */ 200 | delete marketToExit.accountMembership[msg.sender]; 201 | 202 | /* Delete cToken from the account’s list of assets */ 203 | // load into memory for faster iteration 204 | CToken[] memory userAssetList = accountAssets[msg.sender]; 205 | uint len = userAssetList.length; 206 | uint assetIndex = len; 207 | for (uint i = 0; i < len; i++) { 208 | if (userAssetList[i] == cToken) { 209 | assetIndex = i; 210 | break; 211 | } 212 | } 213 | 214 | // We *must* have found the asset in the list or our redundant data structure is broken 215 | assert(assetIndex < len); 216 | 217 | // copy last item in list to location of item to be removed, reduce length by 1 218 | CToken[] storage storedList = accountAssets[msg.sender]; 219 | storedList[assetIndex] = storedList[storedList.length - 1]; 220 | storedList.length--; 221 | 222 | emit MarketExited(cToken, msg.sender); 223 | 224 | return uint(Error.NO_ERROR); 225 | } 226 | 227 | /*** Policy Hooks ***/ 228 | 229 | /** 230 | * @notice Checks if the account should be allowed to mint tokens in the given market 231 | * @param cToken The market to verify the mint against 232 | * @param minter The account which would get the minted tokens 233 | * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens 234 | * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) 235 | */ 236 | function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint) { 237 | // Pausing is a very serious situation - we revert to sound the alarms 238 | require(!mintGuardianPaused[cToken], "mint is paused"); 239 | 240 | // Shh - currently unused 241 | minter; 242 | mintAmount; 243 | 244 | if (!markets[cToken].isListed) { 245 | return uint(Error.MARKET_NOT_LISTED); 246 | } 247 | 248 | // Keep the flywheel moving 249 | updateCanSupplyIndex(cToken); 250 | distributeSupplierCan(cToken, minter, false); 251 | 252 | return uint(Error.NO_ERROR); 253 | } 254 | 255 | /** 256 | * @notice Validates mint and reverts on rejection. May emit logs. 257 | * @param cToken Asset being minted 258 | * @param minter The address minting the tokens 259 | * @param actualMintAmount The amount of the underlying asset being minted 260 | * @param mintTokens The number of tokens being minted 261 | */ 262 | function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) external { 263 | // Shh - currently unused 264 | cToken; 265 | minter; 266 | actualMintAmount; 267 | mintTokens; 268 | 269 | // Shh - we don't ever want this hook to be marked pure 270 | if (false) { 271 | maxAssets = maxAssets; 272 | } 273 | } 274 | 275 | /** 276 | * @notice Checks if the account should be allowed to redeem tokens in the given market 277 | * @param cToken The market to verify the redeem against 278 | * @param redeemer The account which would redeem the tokens 279 | * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market 280 | * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) 281 | */ 282 | function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint) { 283 | uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); 284 | if (allowed != uint(Error.NO_ERROR)) { 285 | return allowed; 286 | } 287 | 288 | // Keep the flywheel moving 289 | updateCanSupplyIndex(cToken); 290 | distributeSupplierCan(cToken, redeemer, false); 291 | 292 | return uint(Error.NO_ERROR); 293 | } 294 | 295 | function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) { 296 | if (!markets[cToken].isListed) { 297 | return uint(Error.MARKET_NOT_LISTED); 298 | } 299 | 300 | /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ 301 | if (!markets[cToken].accountMembership[redeemer]) { 302 | return uint(Error.NO_ERROR); 303 | } 304 | 305 | /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ 306 | (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0); 307 | if (err != Error.NO_ERROR) { 308 | return uint(err); 309 | } 310 | if (shortfall > 0) { 311 | return uint(Error.INSUFFICIENT_LIQUIDITY); 312 | } 313 | 314 | return uint(Error.NO_ERROR); 315 | } 316 | 317 | /** 318 | * @notice Validates redeem and reverts on rejection. May emit logs. 319 | * @param cToken Asset being redeemed 320 | * @param redeemer The address redeeming the tokens 321 | * @param redeemAmount The amount of the underlying asset being redeemed 322 | * @param redeemTokens The number of tokens being redeemed 323 | */ 324 | function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external { 325 | // Shh - currently unused 326 | cToken; 327 | redeemer; 328 | 329 | // Require tokens is zero or amount is also zero 330 | if (redeemTokens == 0 && redeemAmount > 0) { 331 | revert("redeemTokens zero"); 332 | } 333 | } 334 | 335 | /** 336 | * @notice Checks if the account should be allowed to borrow the underlying asset of the given market 337 | * @param cToken The market to verify the borrow against 338 | * @param borrower The account which would borrow the asset 339 | * @param borrowAmount The amount of underlying the account would borrow 340 | * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) 341 | */ 342 | function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint) { 343 | // Pausing is a very serious situation - we revert to sound the alarms 344 | require(!borrowGuardianPaused[cToken], "borrow is paused"); 345 | 346 | if (!markets[cToken].isListed) { 347 | return uint(Error.MARKET_NOT_LISTED); 348 | } 349 | 350 | if (!markets[cToken].accountMembership[borrower]) { 351 | // only cTokens may call borrowAllowed if borrower not in market 352 | require(msg.sender == cToken, "sender must be cToken"); 353 | 354 | // attempt to add borrower to the market 355 | Error err = addToMarketInternal(CToken(msg.sender), borrower); 356 | if (err != Error.NO_ERROR) { 357 | return uint(err); 358 | } 359 | 360 | // it should be impossible to break the important invariant 361 | assert(markets[cToken].accountMembership[borrower]); 362 | } 363 | 364 | if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { 365 | return uint(Error.PRICE_ERROR); 366 | } 367 | 368 | (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount); 369 | if (err != Error.NO_ERROR) { 370 | return uint(err); 371 | } 372 | if (shortfall > 0) { 373 | return uint(Error.INSUFFICIENT_LIQUIDITY); 374 | } 375 | 376 | // Keep the flywheel moving 377 | Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); 378 | updateCanBorrowIndex(cToken, borrowIndex); 379 | distributeBorrowerCan(cToken, borrower, borrowIndex, false); 380 | 381 | return uint(Error.NO_ERROR); 382 | } 383 | 384 | /** 385 | * @notice Validates borrow and reverts on rejection. May emit logs. 386 | * @param cToken Asset whose underlying is being borrowed 387 | * @param borrower The address borrowing the underlying 388 | * @param borrowAmount The amount of the underlying asset requested to borrow 389 | */ 390 | function borrowVerify(address cToken, address borrower, uint borrowAmount) external { 391 | // Shh - currently unused 392 | cToken; 393 | borrower; 394 | borrowAmount; 395 | 396 | // Shh - we don't ever want this hook to be marked pure 397 | if (false) { 398 | maxAssets = maxAssets; 399 | } 400 | } 401 | 402 | /** 403 | * @notice Checks if the account should be allowed to repay a borrow in the given market 404 | * @param cToken The market to verify the repay against 405 | * @param payer The account which would repay the asset 406 | * @param borrower The account which would borrowed the asset 407 | * @param repayAmount The amount of the underlying asset the account would repay 408 | * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) 409 | */ 410 | function repayBorrowAllowed( 411 | address cToken, 412 | address payer, 413 | address borrower, 414 | uint repayAmount) external returns (uint) { 415 | // Shh - currently unused 416 | payer; 417 | borrower; 418 | repayAmount; 419 | 420 | if (!markets[cToken].isListed) { 421 | return uint(Error.MARKET_NOT_LISTED); 422 | } 423 | 424 | // Keep the flywheel moving 425 | Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); 426 | updateCanBorrowIndex(cToken, borrowIndex); 427 | distributeBorrowerCan(cToken, borrower, borrowIndex, false); 428 | 429 | return uint(Error.NO_ERROR); 430 | } 431 | 432 | /** 433 | * @notice Validates repayBorrow and reverts on rejection. May emit logs. 434 | * @param cToken Asset being repaid 435 | * @param payer The address repaying the borrow 436 | * @param borrower The address of the borrower 437 | * @param actualRepayAmount The amount of underlying being repaid 438 | */ 439 | function repayBorrowVerify( 440 | address cToken, 441 | address payer, 442 | address borrower, 443 | uint actualRepayAmount, 444 | uint borrowerIndex) external { 445 | // Shh - currently unused 446 | cToken; 447 | payer; 448 | borrower; 449 | actualRepayAmount; 450 | borrowerIndex; 451 | 452 | // Shh - we don't ever want this hook to be marked pure 453 | if (false) { 454 | maxAssets = maxAssets; 455 | } 456 | } 457 | 458 | /** 459 | * @notice Checks if the liquidation should be allowed to occur 460 | * @param cTokenBorrowed Asset which was borrowed by the borrower 461 | * @param cTokenCollateral Asset which was used as collateral and will be seized 462 | * @param liquidator The address repaying the borrow and seizing the collateral 463 | * @param borrower The address of the borrower 464 | * @param repayAmount The amount of underlying being repaid 465 | */ 466 | function liquidateBorrowAllowed( 467 | address cTokenBorrowed, 468 | address cTokenCollateral, 469 | address liquidator, 470 | address borrower, 471 | uint repayAmount) external returns (uint) { 472 | // Shh - currently unused 473 | liquidator; 474 | 475 | if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) { 476 | return uint(Error.MARKET_NOT_LISTED); 477 | } 478 | 479 | /* The borrower must have shortfall in order to be liquidatable */ 480 | (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower); 481 | if (err != Error.NO_ERROR) { 482 | return uint(err); 483 | } 484 | if (shortfall == 0) { 485 | return uint(Error.INSUFFICIENT_SHORTFALL); 486 | } 487 | 488 | /* The liquidator may not repay more than what is allowed by the closeFactor */ 489 | uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); 490 | (MathError mathErr, uint maxClose) = mulScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); 491 | if (mathErr != MathError.NO_ERROR) { 492 | return uint(Error.MATH_ERROR); 493 | } 494 | if (repayAmount > maxClose) { 495 | return uint(Error.TOO_MUCH_REPAY); 496 | } 497 | 498 | return uint(Error.NO_ERROR); 499 | } 500 | 501 | /** 502 | * @notice Validates liquidateBorrow and reverts on rejection. May emit logs. 503 | * @param cTokenBorrowed Asset which was borrowed by the borrower 504 | * @param cTokenCollateral Asset which was used as collateral and will be seized 505 | * @param liquidator The address repaying the borrow and seizing the collateral 506 | * @param borrower The address of the borrower 507 | * @param actualRepayAmount The amount of underlying being repaid 508 | */ 509 | function liquidateBorrowVerify( 510 | address cTokenBorrowed, 511 | address cTokenCollateral, 512 | address liquidator, 513 | address borrower, 514 | uint actualRepayAmount, 515 | uint seizeTokens) external { 516 | // Shh - currently unused 517 | cTokenBorrowed; 518 | cTokenCollateral; 519 | liquidator; 520 | borrower; 521 | actualRepayAmount; 522 | seizeTokens; 523 | 524 | // Shh - we don't ever want this hook to be marked pure 525 | if (false) { 526 | maxAssets = maxAssets; 527 | } 528 | } 529 | 530 | /** 531 | * @notice Checks if the seizing of assets should be allowed to occur 532 | * @param cTokenCollateral Asset which was used as collateral and will be seized 533 | * @param cTokenBorrowed Asset which was borrowed by the borrower 534 | * @param liquidator The address repaying the borrow and seizing the collateral 535 | * @param borrower The address of the borrower 536 | * @param seizeTokens The number of collateral tokens to seize 537 | */ 538 | function seizeAllowed( 539 | address cTokenCollateral, 540 | address cTokenBorrowed, 541 | address liquidator, 542 | address borrower, 543 | uint seizeTokens) external returns (uint) { 544 | // Pausing is a very serious situation - we revert to sound the alarms 545 | require(!seizeGuardianPaused, "seize is paused"); 546 | 547 | // Shh - currently unused 548 | seizeTokens; 549 | 550 | if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) { 551 | return uint(Error.MARKET_NOT_LISTED); 552 | } 553 | 554 | if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) { 555 | return uint(Error.ChannelsTROLLER_MISMATCH); 556 | } 557 | 558 | // Keep the flywheel moving 559 | updateCanSupplyIndex(cTokenCollateral); 560 | distributeSupplierCan(cTokenCollateral, borrower, false); 561 | distributeSupplierCan(cTokenCollateral, liquidator, false); 562 | 563 | return uint(Error.NO_ERROR); 564 | } 565 | 566 | /** 567 | * @notice Validates seize and reverts on rejection. May emit logs. 568 | * @param cTokenCollateral Asset which was used as collateral and will be seized 569 | * @param cTokenBorrowed Asset which was borrowed by the borrower 570 | * @param liquidator The address repaying the borrow and seizing the collateral 571 | * @param borrower The address of the borrower 572 | * @param seizeTokens The number of collateral tokens to seize 573 | */ 574 | function seizeVerify( 575 | address cTokenCollateral, 576 | address cTokenBorrowed, 577 | address liquidator, 578 | address borrower, 579 | uint seizeTokens) external { 580 | // Shh - currently unused 581 | cTokenCollateral; 582 | cTokenBorrowed; 583 | liquidator; 584 | borrower; 585 | seizeTokens; 586 | 587 | // Shh - we don't ever want this hook to be marked pure 588 | if (false) { 589 | maxAssets = maxAssets; 590 | } 591 | } 592 | 593 | /** 594 | * @notice Checks if the account should be allowed to transfer tokens in the given market 595 | * @param cToken The market to verify the transfer against 596 | * @param src The account which sources the tokens 597 | * @param dst The account which receives the tokens 598 | * @param transferTokens The number of cTokens to transfer 599 | * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) 600 | */ 601 | function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint) { 602 | // Pausing is a very serious situation - we revert to sound the alarms 603 | require(!transferGuardianPaused, "transfer is paused"); 604 | 605 | // Currently the only consideration is whether or not 606 | // the src is allowed to redeem this many tokens 607 | uint allowed = redeemAllowedInternal(cToken, src, transferTokens); 608 | if (allowed != uint(Error.NO_ERROR)) { 609 | return allowed; 610 | } 611 | 612 | // Keep the flywheel moving 613 | updateCanSupplyIndex(cToken); 614 | distributeSupplierCan(cToken, src, false); 615 | distributeSupplierCan(cToken, dst, false); 616 | 617 | return uint(Error.NO_ERROR); 618 | } 619 | 620 | /** 621 | * @notice Validates transfer and reverts on rejection. May emit logs. 622 | * @param cToken Asset being transferred 623 | * @param src The account which sources the tokens 624 | * @param dst The account which receives the tokens 625 | * @param transferTokens The number of cTokens to transfer 626 | */ 627 | function transferVerify(address cToken, address src, address dst, uint transferTokens) external { 628 | // Shh - currently unused 629 | cToken; 630 | src; 631 | dst; 632 | transferTokens; 633 | 634 | // Shh - we don't ever want this hook to be marked pure 635 | if (false) { 636 | maxAssets = maxAssets; 637 | } 638 | } 639 | 640 | /*** Liquidity/Liquidation Calculations ***/ 641 | 642 | /** 643 | * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. 644 | * Note that `cTokenBalance` is the number of cTokens the account owns in the market, 645 | * whereas `borrowBalance` is the amount of underlying that the account has borrowed. 646 | */ 647 | struct AccountLiquidityLocalVars { 648 | uint sumCollateral; 649 | uint sumBorrowPlusEffects; 650 | uint cTokenBalance; 651 | uint borrowBalance; 652 | uint exchangeRateMantissa; 653 | uint oraclePriceMantissa; 654 | Exp collateralFactor; 655 | Exp exchangeRate; 656 | Exp oraclePrice; 657 | Exp tokensToDenom; 658 | } 659 | 660 | /** 661 | * @notice Determine the current account liquidity wrt collateral requirements 662 | * @return (possible error code (semi-opaque), 663 | account liquidity in excess of collateral requirements, 664 | * account shortfall below collateral requirements) 665 | */ 666 | function getAccountLiquidity(address account) public view returns (uint, uint, uint) { 667 | (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); 668 | 669 | return (uint(err), liquidity, shortfall); 670 | } 671 | 672 | /** 673 | * @notice Determine the current account liquidity wrt collateral requirements 674 | * @return (possible error code, 675 | account liquidity in excess of collateral requirements, 676 | * account shortfall below collateral requirements) 677 | */ 678 | function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) { 679 | return getHypotheticalAccountLiquidityInternal(account, CToken(0), 0, 0); 680 | } 681 | 682 | /** 683 | * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed 684 | * @param cTokenModify The market to hypothetically redeem/borrow in 685 | * @param account The account to determine liquidity for 686 | * @param redeemTokens The number of tokens to hypothetically redeem 687 | * @param borrowAmount The amount of underlying to hypothetically borrow 688 | * @return (possible error code (semi-opaque), 689 | hypothetical account liquidity in excess of collateral requirements, 690 | * hypothetical account shortfall below collateral requirements) 691 | */ 692 | function getHypotheticalAccountLiquidity( 693 | address account, 694 | address cTokenModify, 695 | uint redeemTokens, 696 | uint borrowAmount) public view returns (uint, uint, uint) { 697 | (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); 698 | return (uint(err), liquidity, shortfall); 699 | } 700 | 701 | /** 702 | * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed 703 | * @param cTokenModify The market to hypothetically redeem/borrow in 704 | * @param account The account to determine liquidity for 705 | * @param redeemTokens The number of tokens to hypothetically redeem 706 | * @param borrowAmount The amount of underlying to hypothetically borrow 707 | * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, 708 | * without calculating accumulated interest. 709 | * @return (possible error code, 710 | hypothetical account liquidity in excess of collateral requirements, 711 | * hypothetical account shortfall below collateral requirements) 712 | */ 713 | function getHypotheticalAccountLiquidityInternal( 714 | address account, 715 | CToken cTokenModify, 716 | uint redeemTokens, 717 | uint borrowAmount) internal view returns (Error, uint, uint) { 718 | 719 | AccountLiquidityLocalVars memory vars; // Holds all our calculation results 720 | uint oErr; 721 | MathError mErr; 722 | 723 | // For each asset the account is in 724 | CToken[] memory assets = accountAssets[account]; 725 | for (uint i = 0; i < assets.length; i++) { 726 | CToken asset = assets[i]; 727 | 728 | // Read the balances and exchange rate from the cToken 729 | (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); 730 | if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades 731 | return (Error.SNAPSHOT_ERROR, 0, 0); 732 | } 733 | vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); 734 | vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); 735 | 736 | // Get the normalized price of the asset 737 | vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); 738 | if (vars.oraclePriceMantissa == 0) { 739 | return (Error.PRICE_ERROR, 0, 0); 740 | } 741 | vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); 742 | 743 | // Pre-canute a conversion factor from tokens -> ht (normalized price value) 744 | (mErr, vars.tokensToDenom) = mulExp3(vars.collateralFactor, vars.exchangeRate, vars.oraclePrice); 745 | if (mErr != MathError.NO_ERROR) { 746 | return (Error.MATH_ERROR, 0, 0); 747 | } 748 | 749 | // sumCollateral += tokensToDenom * cTokenBalance 750 | (mErr, vars.sumCollateral) = mulScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral); 751 | if (mErr != MathError.NO_ERROR) { 752 | return (Error.MATH_ERROR, 0, 0); 753 | } 754 | 755 | // sumBorrowPlusEffects += oraclePrice * borrowBalance 756 | (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); 757 | if (mErr != MathError.NO_ERROR) { 758 | return (Error.MATH_ERROR, 0, 0); 759 | } 760 | 761 | // Calculate effects of interacting with cTokenModify 762 | if (asset == cTokenModify) { 763 | // redeem effect 764 | // sumBorrowPlusEffects += tokensToDenom * redeemTokens 765 | (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); 766 | if (mErr != MathError.NO_ERROR) { 767 | return (Error.MATH_ERROR, 0, 0); 768 | } 769 | 770 | // borrow effect 771 | // sumBorrowPlusEffects += oraclePrice * borrowAmount 772 | (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); 773 | if (mErr != MathError.NO_ERROR) { 774 | return (Error.MATH_ERROR, 0, 0); 775 | } 776 | } 777 | } 778 | 779 | // These are safe, as the underflow condition is checked first 780 | if (vars.sumCollateral > vars.sumBorrowPlusEffects) { 781 | return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0); 782 | } else { 783 | return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral); 784 | } 785 | } 786 | 787 | /** 788 | * @notice Calculate number of tokens of collateral asset to seize given an underlying amount 789 | * @dev Used in liquidation (called in cToken.liquidateBorrowFresh) 790 | * @param cTokenBorrowed The address of the borrowed cToken 791 | * @param cTokenCollateral The address of the collateral cToken 792 | * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens 793 | * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) 794 | */ 795 | function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { 796 | /* Read oracle prices for borrowed and collateral markets */ 797 | uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); 798 | uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); 799 | if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { 800 | return (uint(Error.PRICE_ERROR), 0); 801 | } 802 | 803 | /* 804 | * Get the exchange rate and calculate the number of collateral tokens to seize: 805 | * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral 806 | * seizeTokens = seizeAmount / exchangeRate 807 | * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) 808 | */ 809 | uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error 810 | uint seizeTokens; 811 | Exp memory numerator; 812 | Exp memory denominator; 813 | Exp memory ratio; 814 | MathError mathErr; 815 | 816 | (mathErr, numerator) = mulExp(liquidationIncentiveMantissa, priceBorrowedMantissa); 817 | if (mathErr != MathError.NO_ERROR) { 818 | return (uint(Error.MATH_ERROR), 0); 819 | } 820 | 821 | (mathErr, denominator) = mulExp(priceCollateralMantissa, exchangeRateMantissa); 822 | if (mathErr != MathError.NO_ERROR) { 823 | return (uint(Error.MATH_ERROR), 0); 824 | } 825 | 826 | (mathErr, ratio) = divExp(numerator, denominator); 827 | if (mathErr != MathError.NO_ERROR) { 828 | return (uint(Error.MATH_ERROR), 0); 829 | } 830 | 831 | (mathErr, seizeTokens) = mulScalarTruncate(ratio, actualRepayAmount); 832 | if (mathErr != MathError.NO_ERROR) { 833 | return (uint(Error.MATH_ERROR), 0); 834 | } 835 | 836 | return (uint(Error.NO_ERROR), seizeTokens); 837 | } 838 | 839 | /*** Admin Functions ***/ 840 | 841 | /** 842 | * @notice Sets a new price oracle for the comptroller 843 | * @dev Admin function to set a new price oracle 844 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 845 | */ 846 | function _setPriceOracle(PriceOracle newOracle) public returns (uint) { 847 | // Check caller is admin 848 | if (msg.sender != admin) { 849 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); 850 | } 851 | 852 | // Track the old oracle for the comptroller 853 | PriceOracle oldOracle = oracle; 854 | 855 | // Set comptroller's oracle to newOracle 856 | oracle = newOracle; 857 | 858 | // Emit NewPriceOracle(oldOracle, newOracle) 859 | emit NewPriceOracle(oldOracle, newOracle); 860 | 861 | return uint(Error.NO_ERROR); 862 | } 863 | 864 | /** 865 | * @notice Sets the closeFactor used when liquidating borrows 866 | * @dev Admin function to set closeFactor 867 | * @param newCloseFactorMantissa New close factor, scaled by 1e18 868 | * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) 869 | */ 870 | function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { 871 | // Check caller is admin 872 | if (msg.sender != admin) { 873 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK); 874 | } 875 | 876 | Exp memory newCloseFactorExp = Exp({mantissa: newCloseFactorMantissa}); 877 | Exp memory lowLimit = Exp({mantissa: closeFactorMinMantissa}); 878 | if (lessThanOrEqualExp(newCloseFactorExp, lowLimit)) { 879 | return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); 880 | } 881 | 882 | Exp memory highLimit = Exp({mantissa: closeFactorMaxMantissa}); 883 | if (lessThanExp(highLimit, newCloseFactorExp)) { 884 | return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); 885 | } 886 | 887 | uint oldCloseFactorMantissa = closeFactorMantissa; 888 | closeFactorMantissa = newCloseFactorMantissa; 889 | emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); 890 | 891 | return uint(Error.NO_ERROR); 892 | } 893 | 894 | /** 895 | * @notice Sets the collateralFactor for a market 896 | * @dev Admin function to set per-market collateralFactor 897 | * @param cToken The market to set the factor on 898 | * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 899 | * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) 900 | */ 901 | function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) { 902 | // Check caller is admin 903 | if (msg.sender != admin) { 904 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK); 905 | } 906 | 907 | // Verify market is listed 908 | Market storage market = markets[address(cToken)]; 909 | if (!market.isListed) { 910 | return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS); 911 | } 912 | 913 | Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); 914 | 915 | // Check collateral factor <= 0.9 916 | Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); 917 | if (lessThanExp(highLimit, newCollateralFactorExp)) { 918 | return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); 919 | } 920 | 921 | // If collateral factor != 0, fail if price == 0 922 | if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0) { 923 | return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); 924 | } 925 | 926 | // Set market's collateral factor to new collateral factor, remember old value 927 | uint oldCollateralFactorMantissa = market.collateralFactorMantissa; 928 | market.collateralFactorMantissa = newCollateralFactorMantissa; 929 | 930 | // Emit event with asset, old collateral factor, and new collateral factor 931 | emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); 932 | 933 | return uint(Error.NO_ERROR); 934 | } 935 | 936 | /** 937 | * @notice Sets maxAssets which controls how many markets can be entered 938 | * @dev Admin function to set maxAssets 939 | * @param newMaxAssets New max assets 940 | * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) 941 | */ 942 | function _setMaxAssets(uint newMaxAssets) external returns (uint) { 943 | // Check caller is admin 944 | if (msg.sender != admin) { 945 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_MAX_ASSETS_OWNER_CHECK); 946 | } 947 | 948 | uint oldMaxAssets = maxAssets; 949 | maxAssets = newMaxAssets; 950 | emit NewMaxAssets(oldMaxAssets, newMaxAssets); 951 | 952 | return uint(Error.NO_ERROR); 953 | } 954 | 955 | /** 956 | * @notice Sets liquidationIncentive 957 | * @dev Admin function to set liquidationIncentive 958 | * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 959 | * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) 960 | */ 961 | function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) { 962 | // Check caller is admin 963 | if (msg.sender != admin) { 964 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK); 965 | } 966 | 967 | // Check de-scaled min <= newLiquidationIncentive <= max 968 | Exp memory newLiquidationIncentive = Exp({mantissa: newLiquidationIncentiveMantissa}); 969 | Exp memory minLiquidationIncentive = Exp({mantissa: liquidationIncentiveMinMantissa}); 970 | if (lessThanExp(newLiquidationIncentive, minLiquidationIncentive)) { 971 | return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION); 972 | } 973 | 974 | Exp memory maxLiquidationIncentive = Exp({mantissa: liquidationIncentiveMaxMantissa}); 975 | if (lessThanExp(maxLiquidationIncentive, newLiquidationIncentive)) { 976 | return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION); 977 | } 978 | 979 | // Save current value for use in log 980 | uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; 981 | 982 | // Set liquidation incentive to new incentive 983 | liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; 984 | 985 | // Emit event with old incentive, new incentive 986 | emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); 987 | 988 | return uint(Error.NO_ERROR); 989 | } 990 | 991 | /** 992 | * @notice Add the market to the markets mapping and set it as listed 993 | * @dev Admin function to set isListed and add support for the market 994 | * @param cToken The address of the market (token) to list 995 | * @return uint 0=success, otherwise a failure. (See enum Error for details) 996 | */ 997 | function _supportMarket(CToken cToken) external returns (uint) { 998 | if (msg.sender != admin) { 999 | return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); 1000 | } 1001 | 1002 | if (markets[address(cToken)].isListed) { 1003 | return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); 1004 | } 1005 | 1006 | cToken.isCToken(); // Sanity check to make sure its really a CToken 1007 | 1008 | markets[address(cToken)] = Market({isListed: true, isCaned: false, collateralFactorMantissa: 0}); 1009 | 1010 | _addMarketInternal(address(cToken)); 1011 | 1012 | emit MarketListed(cToken); 1013 | 1014 | return uint(Error.NO_ERROR); 1015 | } 1016 | 1017 | function _addMarketInternal(address cToken) internal { 1018 | for (uint i = 0; i < allMarkets.length; i ++) { 1019 | require(allMarkets[i] != CToken(cToken), "market already added"); 1020 | } 1021 | allMarkets.push(CToken(cToken)); 1022 | } 1023 | 1024 | /** 1025 | * @notice Admin function to change the Pause Guardian 1026 | * @param newPauseGuardian The address of the new Pause Guardian 1027 | * @return uint 0=success, otherwise a failure. (See enum Error for details) 1028 | */ 1029 | function _setPauseGuardian(address newPauseGuardian) public returns (uint) { 1030 | if (msg.sender != admin) { 1031 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK); 1032 | } 1033 | 1034 | // Save current value for inclusion in log 1035 | address oldPauseGuardian = pauseGuardian; 1036 | 1037 | // Store pauseGuardian with value newPauseGuardian 1038 | pauseGuardian = newPauseGuardian; 1039 | 1040 | // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) 1041 | emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); 1042 | 1043 | return uint(Error.NO_ERROR); 1044 | } 1045 | 1046 | function _setMintPaused(CToken cToken, bool state) public returns (bool) { 1047 | require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); 1048 | require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); 1049 | require(msg.sender == admin || state == true, "only admin can unpause"); 1050 | 1051 | mintGuardianPaused[address(cToken)] = state; 1052 | emit ActionPaused(cToken, "Mint", state); 1053 | return state; 1054 | } 1055 | 1056 | function _setBorrowPaused(CToken cToken, bool state) public returns (bool) { 1057 | require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); 1058 | require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); 1059 | require(msg.sender == admin || state == true, "only admin can unpause"); 1060 | 1061 | borrowGuardianPaused[address(cToken)] = state; 1062 | emit ActionPaused(cToken, "Borrow", state); 1063 | return state; 1064 | } 1065 | 1066 | function _setTransferPaused(bool state) public returns (bool) { 1067 | require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); 1068 | require(msg.sender == admin || state == true, "only admin can unpause"); 1069 | 1070 | transferGuardianPaused = state; 1071 | emit ActionPaused("Transfer", state); 1072 | return state; 1073 | } 1074 | 1075 | function _setSeizePaused(bool state) public returns (bool) { 1076 | require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); 1077 | require(msg.sender == admin || state == true, "only admin can unpause"); 1078 | 1079 | seizeGuardianPaused = state; 1080 | emit ActionPaused("Seize", state); 1081 | return state; 1082 | } 1083 | 1084 | function _become(Unitroller unitroller) public { 1085 | require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); 1086 | require(unitroller._acceptImplementation() == 0, "change not authorized"); 1087 | } 1088 | 1089 | /** 1090 | * @notice Checks caller is admin, or this contract is becoming the new implementation 1091 | */ 1092 | function adminOrInitializing() internal view returns (bool) { 1093 | return msg.sender == admin || msg.sender == comptrollerImplementation; 1094 | } 1095 | 1096 | /*** Can Distribution ***/ 1097 | 1098 | /** 1099 | * @notice Recalculate and update Channels speeds for all Channels markets 1100 | */ 1101 | function refreshCanSpeeds() public { 1102 | require(msg.sender == tx.origin, "only externally owned accounts may refresh speeds"); 1103 | refreshCanSpeedsInternal(); 1104 | } 1105 | 1106 | function refreshCanSpeedsInternal() internal { 1107 | CToken[] memory allMarkets_ = allMarkets; 1108 | 1109 | for (uint i = 0; i < allMarkets_.length; i++) { 1110 | CToken cToken = allMarkets_[i]; 1111 | Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); 1112 | updateCanSupplyIndex(address(cToken)); 1113 | updateCanBorrowIndex(address(cToken), borrowIndex); 1114 | } 1115 | 1116 | Exp memory totalUtility = Exp({mantissa: 0}); 1117 | Exp[] memory utilities = new Exp[](allMarkets_.length); 1118 | for (uint i = 0; i < allMarkets_.length; i++) { 1119 | CToken cToken = allMarkets_[i]; 1120 | if (markets[address(cToken)].isCaned) { 1121 | Exp memory assetPrice = Exp({mantissa: oracle.getUnderlyingPrice(cToken)}); 1122 | Exp memory utility = mul_(assetPrice, cToken.totalBorrows()); 1123 | utilities[i] = utility; 1124 | totalUtility = add_(totalUtility, utility); 1125 | } 1126 | } 1127 | 1128 | for (uint i = 0; i < allMarkets_.length; i++) { 1129 | CToken cToken = allMarkets[i]; 1130 | uint newSpeed = totalUtility.mantissa > 0 ? mul_(canRate, div_(utilities[i], totalUtility)) : 0; 1131 | canSpeeds[address(cToken)] = newSpeed; 1132 | emit CanSpeedUpdated(cToken, newSpeed); 1133 | } 1134 | } 1135 | 1136 | /** 1137 | * @notice Accrue Channels to the market by updating the supply index 1138 | * @param cToken The market whose supply index to update 1139 | */ 1140 | function updateCanSupplyIndex(address cToken) internal { 1141 | CanMarketState storage supplyState = canSupplyState[cToken]; 1142 | uint supplySpeed = canSpeeds[cToken]; 1143 | uint blockNumber = getBlockNumber(); 1144 | uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); 1145 | if (deltaBlocks > 0 && supplySpeed > 0) { 1146 | uint supplyTokens = CToken(cToken).totalSupply(); 1147 | uint canAccrued = mul_(deltaBlocks, supplySpeed); 1148 | Double memory ratio = supplyTokens > 0 ? fraction(canAccrued, supplyTokens) : Double({mantissa: 0}); 1149 | Double memory index = add_(Double({mantissa: supplyState.index}), ratio); 1150 | canSupplyState[cToken] = CanMarketState({ 1151 | index: safe224(index.mantissa, "new index exceeds 224 bits"), 1152 | block: safe32(blockNumber, "block number exceeds 32 bits") 1153 | }); 1154 | } else if (deltaBlocks > 0) { 1155 | supplyState.block = safe32(blockNumber, "block number exceeds 32 bits"); 1156 | } 1157 | } 1158 | 1159 | /** 1160 | * @notice Accrue Channels to the market by updating the borrow index 1161 | * @param cToken The market whose borrow index to update 1162 | */ 1163 | function updateCanBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { 1164 | CanMarketState storage borrowState = canBorrowState[cToken]; 1165 | uint borrowSpeed = canSpeeds[cToken]; 1166 | uint blockNumber = getBlockNumber(); 1167 | uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); 1168 | if (deltaBlocks > 0 && borrowSpeed > 0) { 1169 | uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); 1170 | uint canAccrued = mul_(deltaBlocks, borrowSpeed); 1171 | Double memory ratio = borrowAmount > 0 ? fraction(canAccrued, borrowAmount) : Double({mantissa: 0}); 1172 | Double memory index = add_(Double({mantissa: borrowState.index}), ratio); 1173 | canBorrowState[cToken] = CanMarketState({ 1174 | index: safe224(index.mantissa, "new index exceeds 224 bits"), 1175 | block: safe32(blockNumber, "block number exceeds 32 bits") 1176 | }); 1177 | } else if (deltaBlocks > 0) { 1178 | borrowState.block = safe32(blockNumber, "block number exceeds 32 bits"); 1179 | } 1180 | } 1181 | 1182 | /** 1183 | * @notice Calculate Channels accrued by a supplier and possibly transfer it to them 1184 | * @param cToken The market in which the supplier is interacting 1185 | * @param supplier The address of the supplier to distribute Channels to 1186 | */ 1187 | function distributeSupplierCan(address cToken, address supplier, bool distributeAll) internal { 1188 | CanMarketState storage supplyState = canSupplyState[cToken]; 1189 | Double memory supplyIndex = Double({mantissa: supplyState.index}); 1190 | Double memory supplierIndex = Double({mantissa: canSupplierIndex[cToken][supplier]}); 1191 | canSupplierIndex[cToken][supplier] = supplyIndex.mantissa; 1192 | 1193 | if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { 1194 | supplierIndex.mantissa = canInitialIndex; 1195 | } 1196 | 1197 | Double memory deltaIndex = sub_(supplyIndex, supplierIndex); 1198 | uint supplierTokens = CToken(cToken).balanceOf(supplier); 1199 | uint supplierDelta = mul_(supplierTokens, deltaIndex); 1200 | uint supplierAccrued = add_(canAccrued[supplier], supplierDelta); 1201 | canAccrued[supplier] = transferCan(supplier, supplierAccrued, distributeAll ? 0 : canClaimThreshold); 1202 | emit DistributedSupplierCan(CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa); 1203 | } 1204 | 1205 | /** 1206 | * @notice Calculate Channels accrued by a borrower and possibly transfer it to them 1207 | * @dev Borrowers will not begin to accrue until after the first interaction with the protocol. 1208 | * @param cToken The market in which the borrower is interacting 1209 | * @param borrower The address of the borrower to distribute Channels to 1210 | */ 1211 | function distributeBorrowerCan(address cToken, address borrower, Exp memory marketBorrowIndex, bool distributeAll) internal { 1212 | CanMarketState storage borrowState = canBorrowState[cToken]; 1213 | Double memory borrowIndex = Double({mantissa: borrowState.index}); 1214 | Double memory borrowerIndex = Double({mantissa: canBorrowerIndex[cToken][borrower]}); 1215 | canBorrowerIndex[cToken][borrower] = borrowIndex.mantissa; 1216 | 1217 | if (borrowerIndex.mantissa > 0) { 1218 | Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); 1219 | uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); 1220 | uint borrowerDelta = mul_(borrowerAmount, deltaIndex); 1221 | uint borrowerAccrued = add_(canAccrued[borrower], borrowerDelta); 1222 | canAccrued[borrower] = transferCan(borrower, borrowerAccrued, distributeAll ? 0 : canClaimThreshold); 1223 | emit DistributedBorrowerCan(CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa); 1224 | } 1225 | } 1226 | 1227 | /** 1228 | * @notice Transfer Channels to the user, if they are above the threshold 1229 | * @dev Note: If there is not enough Channels, we do not perform the transfer all. 1230 | * @param user The address of the user to transfer Channels to 1231 | * @param userAccrued The amount of Channels to (possibly) transfer 1232 | * @return The amount of Channels which was NOT transferred to the user 1233 | */ 1234 | function transferCan(address user, uint userAccrued, uint threshold) internal returns (uint) { 1235 | if (userAccrued >= threshold && userAccrued > 0) { 1236 | Can can = Can(getCanAddress()); 1237 | uint canRemaining = can.balanceOf(address(this)); 1238 | if (userAccrued <= canRemaining) { 1239 | can.transfer(user, userAccrued); 1240 | return 0; 1241 | } 1242 | } 1243 | return userAccrued; 1244 | } 1245 | 1246 | /** 1247 | * @notice Claim all the can accrued by holder in all markets 1248 | * @param holder The address to claim Channels for 1249 | */ 1250 | function claimCan(address holder) public { 1251 | return claimCan(holder, allMarkets); 1252 | } 1253 | 1254 | /** 1255 | * @notice Claim all the can accrued by holder in the specified markets 1256 | * @param holder The address to claim Channels for 1257 | * @param cTokens The list of markets to claim Channels in 1258 | */ 1259 | function claimCan(address holder, CToken[] memory cTokens) public { 1260 | address[] memory holders = new address[](1); 1261 | holders[0] = holder; 1262 | claimCan(holders, cTokens, true, true); 1263 | } 1264 | 1265 | /** 1266 | * @notice Claim all can accrued by the holders 1267 | * @param holders The addresses to claim Channels for 1268 | * @param cTokens The list of markets to claim Channels in 1269 | * @param borrowers Whether or not to claim Channels earned by borrowing 1270 | * @param suppliers Whether or not to claim Channels earned by supplying 1271 | */ 1272 | function claimCan(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { 1273 | for (uint i = 0; i < cTokens.length; i++) { 1274 | CToken cToken = cTokens[i]; 1275 | require(markets[address(cToken)].isListed, "market must be listed"); 1276 | if (borrowers == true) { 1277 | Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); 1278 | updateCanBorrowIndex(address(cToken), borrowIndex); 1279 | for (uint j = 0; j < holders.length; j++) { 1280 | distributeBorrowerCan(address(cToken), holders[j], borrowIndex, true); 1281 | } 1282 | } 1283 | if (suppliers == true) { 1284 | updateCanSupplyIndex(address(cToken)); 1285 | for (uint j = 0; j < holders.length; j++) { 1286 | distributeSupplierCan(address(cToken), holders[j], true); 1287 | } 1288 | } 1289 | } 1290 | } 1291 | 1292 | /*** Can Distribution Admin ***/ 1293 | 1294 | /** 1295 | * @notice Set the amount of Channels distributed per block 1296 | * @param canRate_ The amount of Channels wei per block to distribute 1297 | */ 1298 | function _setCanRate(uint canRate_) public { 1299 | require(adminOrInitializing(), "only admin can change can rate"); 1300 | 1301 | uint oldRate = canRate; 1302 | canRate = canRate_; 1303 | emit NewCanRate(oldRate, canRate_); 1304 | 1305 | refreshCanSpeedsInternal(); 1306 | } 1307 | 1308 | /** 1309 | * @notice Add markets to canMarkets, allowing them to earn Channels in the flywheel 1310 | * @param cTokens The addresses of the markets to add 1311 | */ 1312 | function _addCanMarkets(address[] memory cTokens) public { 1313 | require(adminOrInitializing(), "only admin can add can market"); 1314 | 1315 | for (uint i = 0; i < cTokens.length; i++) { 1316 | _addCanMarketInternal(cTokens[i]); 1317 | } 1318 | 1319 | refreshCanSpeedsInternal(); 1320 | } 1321 | 1322 | function _addCanMarketInternal(address cToken) internal { 1323 | Market storage market = markets[cToken]; 1324 | require(market.isListed == true, "can market is not listed"); 1325 | require(market.isCaned == false, "can market already added"); 1326 | 1327 | market.isCaned = true; 1328 | emit MarketCaned(CToken(cToken), true); 1329 | 1330 | if (canSupplyState[cToken].index == 0 && canSupplyState[cToken].block == 0) { 1331 | canSupplyState[cToken] = CanMarketState({ 1332 | index: canInitialIndex, 1333 | block: safe32(getBlockNumber(), "block number exceeds 32 bits") 1334 | }); 1335 | } 1336 | 1337 | if (canBorrowState[cToken].index == 0 && canBorrowState[cToken].block == 0) { 1338 | canBorrowState[cToken] = CanMarketState({ 1339 | index: canInitialIndex, 1340 | block: safe32(getBlockNumber(), "block number exceeds 32 bits") 1341 | }); 1342 | } 1343 | } 1344 | 1345 | /** 1346 | * @notice Remove a market from canMarkets, preventing it from earning Channels in the flywheel 1347 | * @param cToken The address of the market to drop 1348 | */ 1349 | function _dropCanMarket(address cToken) public { 1350 | require(msg.sender == admin, "only admin can drop can market"); 1351 | 1352 | Market storage market = markets[cToken]; 1353 | require(market.isCaned == true, "market is not a can market"); 1354 | 1355 | market.isCaned = false; 1356 | emit MarketCaned(CToken(cToken), false); 1357 | 1358 | refreshCanSpeedsInternal(); 1359 | } 1360 | 1361 | /** 1362 | * @notice Return all of the markets 1363 | * @dev The automatic getter may be used to access an individual market. 1364 | * @return The list of market addresses 1365 | */ 1366 | function getAllMarkets() public view returns (CToken[] memory) { 1367 | return allMarkets; 1368 | } 1369 | 1370 | function getBlockNumber() public view returns (uint) { 1371 | return block.number; 1372 | } 1373 | 1374 | /** 1375 | * @notice Return the address of the Channels token 1376 | * @return The address of Channels 1377 | */ 1378 | function getCanAddress() public view returns (address) { 1379 | return 0x0d14deE0D75D9B2b8cAe378979E5bFca06266cb4; 1380 | } 1381 | } 1382 | -------------------------------------------------------------------------------- /ComptrollerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | contract ComptrollerInterface { 4 | /// @notice Indicator that this is a Comptroller contract (for inspection) 5 | bool public constant isComptroller = true; 6 | 7 | /*** Assets You Are In ***/ 8 | 9 | function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); 10 | function exitMarket(address cToken) external returns (uint); 11 | 12 | /*** Policy Hooks ***/ 13 | 14 | function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); 15 | function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external; 16 | 17 | function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); 18 | function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; 19 | 20 | function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); 21 | function borrowVerify(address cToken, address borrower, uint borrowAmount) external; 22 | 23 | function repayBorrowAllowed( 24 | address cToken, 25 | address payer, 26 | address borrower, 27 | uint repayAmount) external returns (uint); 28 | function repayBorrowVerify( 29 | address cToken, 30 | address payer, 31 | address borrower, 32 | uint repayAmount, 33 | uint borrowerIndex) external; 34 | 35 | function liquidateBorrowAllowed( 36 | address cTokenBorrowed, 37 | address cTokenCollateral, 38 | address liquidator, 39 | address borrower, 40 | uint repayAmount) external returns (uint); 41 | function liquidateBorrowVerify( 42 | address cTokenBorrowed, 43 | address cTokenCollateral, 44 | address liquidator, 45 | address borrower, 46 | uint repayAmount, 47 | uint seizeTokens) external; 48 | 49 | function seizeAllowed( 50 | address cTokenCollateral, 51 | address cTokenBorrowed, 52 | address liquidator, 53 | address borrower, 54 | uint seizeTokens) external returns (uint); 55 | function seizeVerify( 56 | address cTokenCollateral, 57 | address cTokenBorrowed, 58 | address liquidator, 59 | address borrower, 60 | uint seizeTokens) external; 61 | 62 | function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); 63 | function transferVerify(address cToken, address src, address dst, uint transferTokens) external; 64 | 65 | /*** Liquidity/Liquidation Calculations ***/ 66 | 67 | function liquidateCalculateSeizeTokens( 68 | address cTokenBorrowed, 69 | address cTokenCollateral, 70 | uint repayAmount) external view returns (uint, uint); 71 | } 72 | -------------------------------------------------------------------------------- /ComptrollerStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CToken.sol"; 4 | import "./PriceOracle.sol"; 5 | 6 | contract UnitrollerAdminStorage { 7 | /** 8 | * @notice Administrator for this contract 9 | */ 10 | address public admin; 11 | 12 | /** 13 | * @notice Pending administrator for this contract 14 | */ 15 | address public pendingAdmin; 16 | 17 | /** 18 | * @notice Active brains of Unitroller 19 | */ 20 | address public comptrollerImplementation; 21 | 22 | /** 23 | * @notice Pending brains of Unitroller 24 | */ 25 | address public pendingComptrollerImplementation; 26 | } 27 | 28 | contract ComptrollerV1Storage is UnitrollerAdminStorage { 29 | 30 | /** 31 | * @notice Oracle which gives the price of any given asset 32 | */ 33 | PriceOracle public oracle; 34 | 35 | /** 36 | * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow 37 | */ 38 | uint public closeFactorMantissa; 39 | 40 | /** 41 | * @notice Multiplier representing the discount on collateral that a liquidator receives 42 | */ 43 | uint public liquidationIncentiveMantissa; 44 | 45 | /** 46 | * @notice Max number of assets a single account can participate in (borrow or use as collateral) 47 | */ 48 | uint public maxAssets; 49 | 50 | /** 51 | * @notice Per-account mapping of "assets you are in", capped by maxAssets 52 | */ 53 | mapping(address => CToken[]) public accountAssets; 54 | 55 | } 56 | 57 | contract ComptrollerV2Storage is ComptrollerV1Storage { 58 | struct Market { 59 | /// @notice Whether or not this market is listed 60 | bool isListed; 61 | 62 | /** 63 | * @notice Multiplier representing the most one can borrow against their collateral in this market. 64 | * For instance, 0.9 to allow borrowing 90% of collateral value. 65 | * Must be between 0 and 1, and stored as a mantissa. 66 | */ 67 | uint collateralFactorMantissa; 68 | 69 | /// @notice Per-market mapping of "accounts in this asset" 70 | mapping(address => bool) accountMembership; 71 | 72 | /// @notice Whether or not this market receives Channels 73 | bool isCaned; 74 | } 75 | 76 | /** 77 | * @notice Official mapping of cTokens -> Market metadata 78 | * @dev Used e.g. to determine if a market is supported 79 | */ 80 | mapping(address => Market) public markets; 81 | 82 | 83 | /** 84 | * @notice The Pause Guardian can pause certain actions as a safety mechanism. 85 | * Actions which allow users to remove their own assets cannot be paused. 86 | * Liquidation / seizing / transfer can only be paused globally, not by market. 87 | */ 88 | address public pauseGuardian; 89 | bool public _mintGuardianPaused; 90 | bool public _borrowGuardianPaused; 91 | bool public transferGuardianPaused; 92 | bool public seizeGuardianPaused; 93 | mapping(address => bool) public mintGuardianPaused; 94 | mapping(address => bool) public borrowGuardianPaused; 95 | } 96 | 97 | contract ComptrollerV3Storage is ComptrollerV2Storage { 98 | struct CanMarketState { 99 | /// @notice The market's last updated canBorrowIndex or canSupplyIndex 100 | uint224 index; 101 | 102 | /// @notice The block number the index was last updated at 103 | uint32 block; 104 | } 105 | 106 | /// @notice A list of all markets 107 | CToken[] public allMarkets; 108 | 109 | /// @notice The rate at which the flywheel distributes Channels, per block 110 | uint public canRate; 111 | 112 | /// @notice The portion of canRate that each market currently receives 113 | mapping(address => uint) public canSpeeds; 114 | 115 | /// @notice The Channels market supply state for each market 116 | mapping(address => CanMarketState) public canSupplyState; 117 | 118 | /// @notice The Channels market borrow state for each market 119 | mapping(address => CanMarketState) public canBorrowState; 120 | 121 | /// @notice The Channels borrow index for each market for each supplier as of the last time they accrued Channels 122 | mapping(address => mapping(address => uint)) public canSupplierIndex; 123 | 124 | /// @notice The Channels borrow index for each market for each borrower as of the last time they accrued Channels 125 | mapping(address => mapping(address => uint)) public canBorrowerIndex; 126 | 127 | /// @notice The Channels accrued but not yet transferred to each user 128 | mapping(address => uint) public canAccrued; 129 | } 130 | -------------------------------------------------------------------------------- /EIP20Interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | /** 4 | * @title ERC 20 Token Standard Interface 5 | * https://eips.ethereum.org/EIPS/eip-20 6 | */ 7 | interface EIP20Interface { 8 | function name() external view returns (string memory); 9 | function symbol() external view returns (string memory); 10 | function decimals() external view returns (uint8); 11 | 12 | /** 13 | * @notice Get the total number of tokens in circulation 14 | * @return The supply of tokens 15 | */ 16 | function totalSupply() external view returns (uint256); 17 | 18 | /** 19 | * @notice Gets the balance of the specified address 20 | * @param owner The address from which the balance will be retrieved 21 | * @return The balance 22 | */ 23 | function balanceOf(address owner) external view returns (uint256 balance); 24 | 25 | /** 26 | * @notice Transfer `amount` tokens from `msg.sender` to `dst` 27 | * @param dst The address of the destination account 28 | * @param amount The number of tokens to transfer 29 | * @return Whether or not the transfer succeeded 30 | */ 31 | function transfer(address dst, uint256 amount) external returns (bool success); 32 | 33 | /** 34 | * @notice Transfer `amount` tokens from `src` to `dst` 35 | * @param src The address of the source account 36 | * @param dst The address of the destination account 37 | * @param amount The number of tokens to transfer 38 | * @return Whether or not the transfer succeeded 39 | */ 40 | function transferFrom(address src, address dst, uint256 amount) external returns (bool success); 41 | 42 | /** 43 | * @notice Approve `spender` to transfer up to `amount` from `src` 44 | * @dev This will overwrite the approval amount for `spender` 45 | * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 46 | * @param spender The address of the account which may transfer tokens 47 | * @param amount The number of tokens that are approved (-1 means infinite) 48 | * @return Whether or not the approval succeeded 49 | */ 50 | function approve(address spender, uint256 amount) external returns (bool success); 51 | 52 | /** 53 | * @notice Get the current allowance from `owner` for `spender` 54 | * @param owner The address of the account which owns the tokens to be spent 55 | * @param spender The address of the account which may transfer tokens 56 | * @return The number of tokens allowed to be spent (-1 means infinite) 57 | */ 58 | function allowance(address owner, address spender) external view returns (uint256 remaining); 59 | 60 | event Transfer(address indexed from, address indexed to, uint256 amount); 61 | event Approval(address indexed owner, address indexed spender, uint256 amount); 62 | } 63 | -------------------------------------------------------------------------------- /EIP20NonStandardInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | /** 4 | * @title EIP20NonStandardInterface 5 | * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` 6 | * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 7 | */ 8 | interface EIP20NonStandardInterface { 9 | 10 | /** 11 | * @notice Get the total number of tokens in circulation 12 | * @return The supply of tokens 13 | */ 14 | function totalSupply() external view returns (uint256); 15 | 16 | /** 17 | * @notice Gets the balance of the specified address 18 | * @param owner The address from which the balance will be retrieved 19 | * @return The balance 20 | */ 21 | function balanceOf(address owner) external view returns (uint256 balance); 22 | 23 | /// 24 | /// !!!!!!!!!!!!!! 25 | /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification 26 | /// !!!!!!!!!!!!!! 27 | /// 28 | 29 | /** 30 | * @notice Transfer `amount` tokens from `msg.sender` to `dst` 31 | * @param dst The address of the destination account 32 | * @param amount The number of tokens to transfer 33 | */ 34 | function transfer(address dst, uint256 amount) external; 35 | 36 | /// 37 | /// !!!!!!!!!!!!!! 38 | /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification 39 | /// !!!!!!!!!!!!!! 40 | /// 41 | 42 | /** 43 | * @notice Transfer `amount` tokens from `src` to `dst` 44 | * @param src The address of the source account 45 | * @param dst The address of the destination account 46 | * @param amount The number of tokens to transfer 47 | */ 48 | function transferFrom(address src, address dst, uint256 amount) external; 49 | 50 | /** 51 | * @notice Approve `spender` to transfer up to `amount` from `src` 52 | * @dev This will overwrite the approval amount for `spender` 53 | * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 54 | * @param spender The address of the account which may transfer tokens 55 | * @param amount The number of tokens that are approved 56 | * @return Whether or not the approval succeeded 57 | */ 58 | function approve(address spender, uint256 amount) external returns (bool success); 59 | 60 | /** 61 | * @notice Get the current allowance from `owner` for `spender` 62 | * @param owner The address of the account which owns the tokens to be spent 63 | * @param spender The address of the account which may transfer tokens 64 | * @return The number of tokens allowed to be spent 65 | */ 66 | function allowance(address owner, address spender) external view returns (uint256 remaining); 67 | 68 | event Transfer(address indexed from, address indexed to, uint256 amount); 69 | event Approval(address indexed owner, address indexed spender, uint256 amount); 70 | } 71 | -------------------------------------------------------------------------------- /ErrorReporter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | contract ComptrollerErrorReporter { 4 | enum Error { 5 | NO_ERROR, 6 | UNAUTHORIZED, 7 | ChannelsTROLLER_MISMATCH, 8 | INSUFFICIENT_SHORTFALL, 9 | INSUFFICIENT_LIQUIDITY, 10 | INVALID_CLOSE_FACTOR, 11 | INVALID_COLLATERAL_FACTOR, 12 | INVALID_LIQUIDATION_INCENTIVE, 13 | MARKET_NOT_ENTERED, // no longer possible 14 | MARKET_NOT_LISTED, 15 | MARKET_ALREADY_LISTED, 16 | MATH_ERROR, 17 | NONZERO_BORROW_BALANCE, 18 | PRICE_ERROR, 19 | REJECTION, 20 | SNAPSHOT_ERROR, 21 | TOO_MANY_ASSETS, 22 | TOO_MUCH_REPAY 23 | } 24 | 25 | enum FailureInfo { 26 | ACCEPT_ADMIN_PENDING_ADMIN_CHECK, 27 | ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, 28 | EXIT_MARKET_BALANCE_OWED, 29 | EXIT_MARKET_REJECTION, 30 | SET_CLOSE_FACTOR_OWNER_CHECK, 31 | SET_CLOSE_FACTOR_VALIDATION, 32 | SET_COLLATERAL_FACTOR_OWNER_CHECK, 33 | SET_COLLATERAL_FACTOR_NO_EXISTS, 34 | SET_COLLATERAL_FACTOR_VALIDATION, 35 | SET_COLLATERAL_FACTOR_WITHOUT_PRICE, 36 | SET_IMPLEMENTATION_OWNER_CHECK, 37 | SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, 38 | SET_LIQUIDATION_INCENTIVE_VALIDATION, 39 | SET_MAX_ASSETS_OWNER_CHECK, 40 | SET_PENDING_ADMIN_OWNER_CHECK, 41 | SET_PENDING_IMPLEMENTATION_OWNER_CHECK, 42 | SET_PRICE_ORACLE_OWNER_CHECK, 43 | SUPPORT_MARKET_EXISTS, 44 | SUPPORT_MARKET_OWNER_CHECK, 45 | SET_PAUSE_GUARDIAN_OWNER_CHECK 46 | } 47 | 48 | /** 49 | * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary 50 | * contract-specific code that enables us to report opaque error codes from upgradeable contracts. 51 | **/ 52 | event Failure(uint error, uint info, uint detail); 53 | 54 | /** 55 | * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator 56 | */ 57 | function fail(Error err, FailureInfo info) internal returns (uint) { 58 | emit Failure(uint(err), uint(info), 0); 59 | 60 | return uint(err); 61 | } 62 | 63 | /** 64 | * @dev use this when reporting an opaque error from an upgradeable collaborator contract 65 | */ 66 | function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { 67 | emit Failure(uint(err), uint(info), opaqueError); 68 | 69 | return uint(err); 70 | } 71 | } 72 | 73 | contract TokenErrorReporter { 74 | enum Error { 75 | NO_ERROR, 76 | UNAUTHORIZED, 77 | BAD_INPUT, 78 | ChannelsTROLLER_REJECTION, 79 | ChannelsTROLLER_CALCULATION_ERROR, 80 | INTEREST_RATE_MODEL_ERROR, 81 | INVALID_ACCOUNT_PAIR, 82 | INVALID_CLOSE_AMOUNT_REQUESTED, 83 | INVALID_COLLATERAL_FACTOR, 84 | MATH_ERROR, 85 | MARKET_NOT_FRESH, 86 | MARKET_NOT_LISTED, 87 | TOKEN_INSUFFICIENT_ALLOWANCE, 88 | TOKEN_INSUFFICIENT_BALANCE, 89 | TOKEN_INSUFFICIENT_CASH, 90 | TOKEN_TRANSFER_IN_FAILED, 91 | TOKEN_TRANSFER_OUT_FAILED 92 | } 93 | 94 | /* 95 | * Note: FailureInfo (but not Error) is kept in alphabetical order 96 | * This is because FailureInfo grows significantly faster, and 97 | * the order of Error has some meaning, while the order of FailureInfo 98 | * is entirely arbitrary. 99 | */ 100 | enum FailureInfo { 101 | ACCEPT_ADMIN_PENDING_ADMIN_CHECK, 102 | ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, 103 | ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED, 104 | ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, 105 | ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, 106 | ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, 107 | ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, 108 | BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, 109 | BORROW_ACCRUE_INTEREST_FAILED, 110 | BORROW_CASH_NOT_AVAILABLE, 111 | BORROW_FRESHNESS_CHECK, 112 | BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, 113 | BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, 114 | BORROW_MARKET_NOT_LISTED, 115 | BORROW_ChannelsTROLLER_REJECTION, 116 | LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED, 117 | LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED, 118 | LIQUIDATE_COLLATERAL_FRESHNESS_CHECK, 119 | LIQUIDATE_ChannelsTROLLER_REJECTION, 120 | LIQUIDATE_ChannelsTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED, 121 | LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX, 122 | LIQUIDATE_CLOSE_AMOUNT_IS_ZERO, 123 | LIQUIDATE_FRESHNESS_CHECK, 124 | LIQUIDATE_LIQUIDATOR_IS_BORROWER, 125 | LIQUIDATE_REPAY_BORROW_FRESH_FAILED, 126 | LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, 127 | LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, 128 | LIQUIDATE_SEIZE_ChannelsTROLLER_REJECTION, 129 | LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER, 130 | LIQUIDATE_SEIZE_TOO_MUCH, 131 | MINT_ACCRUE_INTEREST_FAILED, 132 | MINT_ChannelsTROLLER_REJECTION, 133 | MINT_EXCHANGE_CALCULATION_FAILED, 134 | MINT_EXCHANGE_RATE_READ_FAILED, 135 | MINT_FRESHNESS_CHECK, 136 | MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, 137 | MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, 138 | MINT_TRANSFER_IN_FAILED, 139 | MINT_TRANSFER_IN_NOT_POSSIBLE, 140 | REDEEM_ACCRUE_INTEREST_FAILED, 141 | REDEEM_ChannelsTROLLER_REJECTION, 142 | REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, 143 | REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, 144 | REDEEM_EXCHANGE_RATE_READ_FAILED, 145 | REDEEM_FRESHNESS_CHECK, 146 | REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, 147 | REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, 148 | REDEEM_TRANSFER_OUT_NOT_POSSIBLE, 149 | REDUCE_RESERVES_ACCRUE_INTEREST_FAILED, 150 | REDUCE_RESERVES_ADMIN_CHECK, 151 | REDUCE_RESERVES_CASH_NOT_AVAILABLE, 152 | REDUCE_RESERVES_FRESH_CHECK, 153 | REDUCE_RESERVES_VALIDATION, 154 | REPAY_BEHALF_ACCRUE_INTEREST_FAILED, 155 | REPAY_BORROW_ACCRUE_INTEREST_FAILED, 156 | REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, 157 | REPAY_BORROW_ChannelsTROLLER_REJECTION, 158 | REPAY_BORROW_FRESHNESS_CHECK, 159 | REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, 160 | REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, 161 | REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE, 162 | SET_COLLATERAL_FACTOR_OWNER_CHECK, 163 | SET_COLLATERAL_FACTOR_VALIDATION, 164 | SET_ChannelsTROLLER_OWNER_CHECK, 165 | SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED, 166 | SET_INTEREST_RATE_MODEL_FRESH_CHECK, 167 | SET_INTEREST_RATE_MODEL_OWNER_CHECK, 168 | SET_MAX_ASSETS_OWNER_CHECK, 169 | SET_ORACLE_MARKET_NOT_LISTED, 170 | SET_PENDING_ADMIN_OWNER_CHECK, 171 | SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED, 172 | SET_RESERVE_FACTOR_ADMIN_CHECK, 173 | SET_RESERVE_FACTOR_FRESH_CHECK, 174 | SET_RESERVE_FACTOR_BOUNDS_CHECK, 175 | TRANSFER_ChannelsTROLLER_REJECTION, 176 | TRANSFER_NOT_ALLOWED, 177 | TRANSFER_NOT_ENOUGH, 178 | TRANSFER_TOO_MUCH, 179 | ADD_RESERVES_ACCRUE_INTEREST_FAILED, 180 | ADD_RESERVES_FRESH_CHECK, 181 | ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE 182 | } 183 | 184 | /** 185 | * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary 186 | * contract-specific code that enables us to report opaque error codes from upgradeable contracts. 187 | **/ 188 | event Failure(uint error, uint info, uint detail); 189 | 190 | /** 191 | * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator 192 | */ 193 | function fail(Error err, FailureInfo info) internal returns (uint) { 194 | emit Failure(uint(err), uint(info), 0); 195 | 196 | return uint(err); 197 | } 198 | 199 | /** 200 | * @dev use this when reporting an opaque error from an upgradeable collaborator contract 201 | */ 202 | function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) { 203 | emit Failure(uint(err), uint(info), opaqueError); 204 | 205 | return uint(err); 206 | } 207 | } -------------------------------------------------------------------------------- /Exponential.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CarefulMath.sol"; 4 | 5 | /** 6 | * @title Exponential module for storing fixed-precision decimals 7 | * @author Channels 8 | * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. 9 | * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: 10 | * `Exp({mantissa: 5100000000000000000})`. 11 | */ 12 | contract Exponential is CarefulMath { 13 | uint constant expScale = 1e18; 14 | uint constant doubleScale = 1e36; 15 | uint constant halfExpScale = expScale/2; 16 | uint constant mantissaOne = expScale; 17 | 18 | struct Exp { 19 | uint mantissa; 20 | } 21 | 22 | struct Double { 23 | uint mantissa; 24 | } 25 | 26 | /** 27 | * @dev Creates an exponential from numerator and denominator values. 28 | * Note: Returns an error if (`num` * 10e18) > MAX_INT, 29 | * or if `denom` is zero. 30 | */ 31 | function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) { 32 | (MathError err0, uint scaledNumerator) = mulUInt(num, expScale); 33 | if (err0 != MathError.NO_ERROR) { 34 | return (err0, Exp({mantissa: 0})); 35 | } 36 | 37 | (MathError err1, uint rational) = divUInt(scaledNumerator, denom); 38 | if (err1 != MathError.NO_ERROR) { 39 | return (err1, Exp({mantissa: 0})); 40 | } 41 | 42 | return (MathError.NO_ERROR, Exp({mantissa: rational})); 43 | } 44 | 45 | /** 46 | * @dev Adds two exponentials, returning a new exponential. 47 | */ 48 | function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { 49 | (MathError error, uint result) = addUInt(a.mantissa, b.mantissa); 50 | 51 | return (error, Exp({mantissa: result})); 52 | } 53 | 54 | /** 55 | * @dev Subtracts two exponentials, returning a new exponential. 56 | */ 57 | function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { 58 | (MathError error, uint result) = subUInt(a.mantissa, b.mantissa); 59 | 60 | return (error, Exp({mantissa: result})); 61 | } 62 | 63 | /** 64 | * @dev Multiply an Exp by a scalar, returning a new Exp. 65 | */ 66 | function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { 67 | (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar); 68 | if (err0 != MathError.NO_ERROR) { 69 | return (err0, Exp({mantissa: 0})); 70 | } 71 | 72 | return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa})); 73 | } 74 | 75 | /** 76 | * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. 77 | */ 78 | function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) { 79 | (MathError err, Exp memory product) = mulScalar(a, scalar); 80 | if (err != MathError.NO_ERROR) { 81 | return (err, 0); 82 | } 83 | 84 | return (MathError.NO_ERROR, truncate(product)); 85 | } 86 | 87 | /** 88 | * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. 89 | */ 90 | function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) { 91 | (MathError err, Exp memory product) = mulScalar(a, scalar); 92 | if (err != MathError.NO_ERROR) { 93 | return (err, 0); 94 | } 95 | 96 | return addUInt(truncate(product), addend); 97 | } 98 | 99 | /** 100 | * @dev Divide an Exp by a scalar, returning a new Exp. 101 | */ 102 | function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) { 103 | (MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar); 104 | if (err0 != MathError.NO_ERROR) { 105 | return (err0, Exp({mantissa: 0})); 106 | } 107 | 108 | return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa})); 109 | } 110 | 111 | /** 112 | * @dev Divide a scalar by an Exp, returning a new Exp. 113 | */ 114 | function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) { 115 | /* 116 | We are doing this as: 117 | getExp(mulUInt(expScale, scalar), divisor.mantissa) 118 | 119 | How it works: 120 | Exp = a / b; 121 | Scalar = s; 122 | `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale` 123 | */ 124 | (MathError err0, uint numerator) = mulUInt(expScale, scalar); 125 | if (err0 != MathError.NO_ERROR) { 126 | return (err0, Exp({mantissa: 0})); 127 | } 128 | return getExp(numerator, divisor.mantissa); 129 | } 130 | 131 | /** 132 | * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer. 133 | */ 134 | function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) { 135 | (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor); 136 | if (err != MathError.NO_ERROR) { 137 | return (err, 0); 138 | } 139 | 140 | return (MathError.NO_ERROR, truncate(fraction)); 141 | } 142 | 143 | /** 144 | * @dev Multiplies two exponentials, returning a new exponential. 145 | */ 146 | function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { 147 | 148 | (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa); 149 | if (err0 != MathError.NO_ERROR) { 150 | return (err0, Exp({mantissa: 0})); 151 | } 152 | 153 | // We add half the scale before dividing so that we get rounding instead of truncation. 154 | // See "Listing 6" and text above it at https://accu.org/index.php/journals/1717 155 | // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18. 156 | (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct); 157 | if (err1 != MathError.NO_ERROR) { 158 | return (err1, Exp({mantissa: 0})); 159 | } 160 | 161 | (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale); 162 | // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero. 163 | assert(err2 == MathError.NO_ERROR); 164 | 165 | return (MathError.NO_ERROR, Exp({mantissa: product})); 166 | } 167 | 168 | /** 169 | * @dev Multiplies two exponentials given their mantissas, returning a new exponential. 170 | */ 171 | function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) { 172 | return mulExp(Exp({mantissa: a}), Exp({mantissa: b})); 173 | } 174 | 175 | /** 176 | * @dev Multiplies three exponentials, returning a new exponential. 177 | */ 178 | function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) { 179 | (MathError err, Exp memory ab) = mulExp(a, b); 180 | if (err != MathError.NO_ERROR) { 181 | return (err, ab); 182 | } 183 | return mulExp(ab, c); 184 | } 185 | 186 | /** 187 | * @dev Divides two exponentials, returning a new exponential. 188 | * (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b, 189 | * which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa) 190 | */ 191 | function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) { 192 | return getExp(a.mantissa, b.mantissa); 193 | } 194 | 195 | /** 196 | * @dev Truncates the given exp to a whole number value. 197 | * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 198 | */ 199 | function truncate(Exp memory exp) pure internal returns (uint) { 200 | // Note: We are not using careful math here as we're performing a division that cannot fail 201 | return exp.mantissa / expScale; 202 | } 203 | 204 | /** 205 | * @dev Checks if first Exp is less than second Exp. 206 | */ 207 | function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { 208 | return left.mantissa < right.mantissa; 209 | } 210 | 211 | /** 212 | * @dev Checks if left Exp <= right Exp. 213 | */ 214 | function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) { 215 | return left.mantissa <= right.mantissa; 216 | } 217 | 218 | /** 219 | * @dev Checks if left Exp > right Exp. 220 | */ 221 | function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) { 222 | return left.mantissa > right.mantissa; 223 | } 224 | 225 | /** 226 | * @dev returns true if Exp is exactly zero 227 | */ 228 | function isZeroExp(Exp memory value) pure internal returns (bool) { 229 | return value.mantissa == 0; 230 | } 231 | 232 | function safe224(uint n, string memory errorMessage) pure internal returns (uint224) { 233 | require(n < 2**224, errorMessage); 234 | return uint224(n); 235 | } 236 | 237 | function safe32(uint n, string memory errorMessage) pure internal returns (uint32) { 238 | require(n < 2**32, errorMessage); 239 | return uint32(n); 240 | } 241 | 242 | function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { 243 | return Exp({mantissa: add_(a.mantissa, b.mantissa)}); 244 | } 245 | 246 | function add_(Double memory a, Double memory b) pure internal returns (Double memory) { 247 | return Double({mantissa: add_(a.mantissa, b.mantissa)}); 248 | } 249 | 250 | function add_(uint a, uint b) pure internal returns (uint) { 251 | return add_(a, b, "addition overflow"); 252 | } 253 | 254 | function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { 255 | uint c = a + b; 256 | require(c >= a, errorMessage); 257 | return c; 258 | } 259 | 260 | function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { 261 | return Exp({mantissa: sub_(a.mantissa, b.mantissa)}); 262 | } 263 | 264 | function sub_(Double memory a, Double memory b) pure internal returns (Double memory) { 265 | return Double({mantissa: sub_(a.mantissa, b.mantissa)}); 266 | } 267 | 268 | function sub_(uint a, uint b) pure internal returns (uint) { 269 | return sub_(a, b, "subtraction underflow"); 270 | } 271 | 272 | function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { 273 | require(b <= a, errorMessage); 274 | return a - b; 275 | } 276 | 277 | function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { 278 | return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale}); 279 | } 280 | 281 | function mul_(Exp memory a, uint b) pure internal returns (Exp memory) { 282 | return Exp({mantissa: mul_(a.mantissa, b)}); 283 | } 284 | 285 | function mul_(uint a, Exp memory b) pure internal returns (uint) { 286 | return mul_(a, b.mantissa) / expScale; 287 | } 288 | 289 | function mul_(Double memory a, Double memory b) pure internal returns (Double memory) { 290 | return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale}); 291 | } 292 | 293 | function mul_(Double memory a, uint b) pure internal returns (Double memory) { 294 | return Double({mantissa: mul_(a.mantissa, b)}); 295 | } 296 | 297 | function mul_(uint a, Double memory b) pure internal returns (uint) { 298 | return mul_(a, b.mantissa) / doubleScale; 299 | } 300 | 301 | function mul_(uint a, uint b) pure internal returns (uint) { 302 | return mul_(a, b, "multiplication overflow"); 303 | } 304 | 305 | function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { 306 | if (a == 0 || b == 0) { 307 | return 0; 308 | } 309 | uint c = a * b; 310 | require(c / a == b, errorMessage); 311 | return c; 312 | } 313 | 314 | function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) { 315 | return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)}); 316 | } 317 | 318 | function div_(Exp memory a, uint b) pure internal returns (Exp memory) { 319 | return Exp({mantissa: div_(a.mantissa, b)}); 320 | } 321 | 322 | function div_(uint a, Exp memory b) pure internal returns (uint) { 323 | return div_(mul_(a, expScale), b.mantissa); 324 | } 325 | 326 | function div_(Double memory a, Double memory b) pure internal returns (Double memory) { 327 | return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)}); 328 | } 329 | 330 | function div_(Double memory a, uint b) pure internal returns (Double memory) { 331 | return Double({mantissa: div_(a.mantissa, b)}); 332 | } 333 | 334 | function div_(uint a, Double memory b) pure internal returns (uint) { 335 | return div_(mul_(a, doubleScale), b.mantissa); 336 | } 337 | 338 | function div_(uint a, uint b) pure internal returns (uint) { 339 | return div_(a, b, "divide by zero"); 340 | } 341 | 342 | function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) { 343 | require(b > 0, errorMessage); 344 | return a / b; 345 | } 346 | 347 | function fraction(uint a, uint b) pure internal returns (Double memory) { 348 | return Double({mantissa: div_(mul_(a, doubleScale), b)}); 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /InterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | /** 4 | * @title Channels's InterestRateModel Interface 5 | * @author Channels 6 | */ 7 | contract InterestRateModel { 8 | /// @notice Indicator that this is an InterestRateModel contract (for inspection) 9 | bool public constant isInterestRateModel = true; 10 | 11 | /** 12 | * @notice Calculates the current borrow interest rate per block 13 | * @param cash The total amount of cash the market has 14 | * @param borrows The total amount of borrows the market has outstanding 15 | * @param reserves The total amnount of reserves the market has 16 | * @return The borrow rate per block (as a percentage, and scaled by 1e18) 17 | */ 18 | function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint); 19 | 20 | /** 21 | * @notice Calculates the current supply interest rate per block 22 | * @param cash The total amount of cash the market has 23 | * @param borrows The total amount of borrows the market has outstanding 24 | * @param reserves The total amnount of reserves the market has 25 | * @param reserveFactorMantissa The current reserve factor the market has 26 | * @return The supply rate per block (as a percentage, and scaled by 1e18) 27 | */ 28 | function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Maximillion.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./CHT.sol"; 4 | 5 | /** 6 | * @title Channels's Maximillion Contract 7 | * @author Channels 8 | */ 9 | contract Maximillion { 10 | /** 11 | * @notice The default cHT market to repay in 12 | */ 13 | CHT public cHT; 14 | 15 | /** 16 | * @notice Construct a Maximillion to repay max in a CHT market 17 | */ 18 | constructor(CHT cHT_) public { 19 | cHT = cHT_; 20 | } 21 | 22 | /** 23 | * @notice msg.sender sends HT to repay an account's borrow in the cHT market 24 | * @dev The provided HT is applied towards the borrow balance, any excess is refunded 25 | * @param borrower The address of the borrower account to repay on behalf of 26 | */ 27 | function repayBehalf(address borrower) public payable { 28 | repayBehalfExplicit(borrower, cHT); 29 | } 30 | 31 | /** 32 | * @notice msg.sender sends HT to repay an account's borrow in a cHT market 33 | * @dev The provided HT is applied towards the borrow balance, any excess is refunded 34 | * @param borrower The address of the borrower account to repay on behalf of 35 | * @param cHT_ The address of the cHT contract to repay in 36 | */ 37 | function repayBehalfExplicit(address borrower, CHT cHT_) public payable { 38 | uint received = msg.value; 39 | uint borrows = cHT_.borrowBalanceCurrent(borrower); 40 | if (received > borrows) { 41 | cHT_.repayBorrowBehalf.value(borrows)(borrower); 42 | msg.sender.transfer(received - borrows); 43 | } else { 44 | cHT_.repayBorrowBehalf.value(received)(borrower); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /OpenOracleData.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.6.10; 3 | pragma experimental ABIEncoderV2; 4 | 5 | /** 6 | * @title The Open Oracle Data Base Contract 7 | * @author Channels Labs, Inc. 8 | */ 9 | contract OpenOracleData { 10 | /** 11 | * @notice The event emitted when a source writes to its storage 12 | */ 13 | //event Write(address indexed source, indexed key, string kind, uint64 timestamp, value); 14 | 15 | /** 16 | * @notice Write a bunch of signed datum to the authenticated storage mapping 17 | * @param message The payload containing the timestamp, and (key, value) pairs 18 | * @param signature The cryptographic signature of the message payload, authorizing the source to write 19 | * @return The keys that were written 20 | */ 21 | //function put(bytes calldata message, bytes calldata signature) external returns ( memory); 22 | 23 | /** 24 | * @notice Read a single key with a pre-defined type signature from an authenticated source 25 | * @param source The verifiable author of the data 26 | * @param key The selector for the value to return 27 | * @return The claimed Unix timestamp for the data and the encoded value (defaults to (0, 0x)) 28 | */ 29 | //function get(address source, key) external view returns (uint, ); 30 | 31 | /** 32 | * @notice Recovers the source address which signed a message 33 | * @dev Comparing to a claimed address would add nothing, 34 | * as the caller could simply perform the recover and claim that address. 35 | * @param message The data that was presumably signed 36 | * @param signature The fingerprint of the data + private key 37 | * @return The source address which signed the message, presumably 38 | */ 39 | function source(bytes memory message, bytes memory signature) public pure returns (address) { 40 | (bytes32 r, bytes32 s, uint8 v) = abi.decode(signature, (bytes32, bytes32, uint8)); 41 | bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(message))); 42 | return ecrecover(hash, v, r, s); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /OpenOraclePriceData.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.6.10; 3 | 4 | import "./OpenOracleData.sol"; 5 | 6 | /** 7 | * @title The Open Oracle Price Data Contract 8 | * @notice Values stored in this contract should represent a USD price with 6 decimals precision 9 | * @author Channels Labs, Inc. 10 | */ 11 | 12 | contract OpenOraclePriceData is OpenOracleData { 13 | ///@notice The event emitted when a source writes to its storage 14 | event Write(address indexed source, string key, uint64 timestamp, uint64 value); 15 | ///@notice The event emitted when the timestamp on a price is invalid and it is not written to storage 16 | event NotWritten(uint64 priorTimestamp, uint256 messageTimestamp, uint256 blockTimestamp); 17 | 18 | ///@notice The fundamental unit of storage for a reporter source 19 | struct Datum { 20 | uint64 timestamp; 21 | uint64 value; 22 | } 23 | 24 | /** 25 | * @dev The most recent authenticated data from all sources. 26 | * This is private because dynamic mapping keys preclude auto-generated getters. 27 | */ 28 | mapping(address => mapping(string => Datum)) private data; 29 | 30 | /** 31 | * @notice Write a bunch of signed datum to the authenticated storage mapping 32 | * @param message The payload containing the timestamp, and (key, value) pairs 33 | * @param signature The cryptographic signature of the message payload, authorizing the source to write 34 | * @return The keys that were written 35 | */ 36 | function put(bytes calldata message, bytes calldata signature) external returns (string memory) { 37 | (address source, uint64 timestamp, string memory key, uint64 value) = decodeMessage(message, signature); 38 | return putInternal(source, timestamp, key, value); 39 | } 40 | 41 | function putInternal(address source, uint64 timestamp, string memory key, uint64 value) internal returns (string memory) { 42 | // Only update if newer than stored, according to source 43 | Datum storage prior = data[source][key]; 44 | if (timestamp > prior.timestamp && timestamp < block.timestamp + 60 minutes && source != address(0)) { 45 | data[source][key] = Datum(timestamp, value); 46 | emit Write(source, key, timestamp, value); 47 | } else { 48 | emit NotWritten(prior.timestamp, timestamp, block.timestamp); 49 | } 50 | return key; 51 | } 52 | 53 | function decodeMessage(bytes calldata message, bytes calldata signature) internal pure returns (address, uint64, string memory, uint64) { 54 | // Recover the source address 55 | address source = source(message, signature); 56 | 57 | // Decode the message and check the kind 58 | (string memory kind, uint64 timestamp, string memory key, uint64 value) = abi.decode(message, (string, uint64, string, uint64)); 59 | require(keccak256(abi.encodePacked(kind)) == keccak256(abi.encodePacked("prices")), "Kind of data must be 'prices'"); 60 | return (source, timestamp, key, value); 61 | } 62 | 63 | /** 64 | * @notice Read a single key from an authenticated source 65 | * @param source The verifiable author of the data 66 | * @param key The selector for the value to return 67 | * @return The claimed Unix timestamp for the data and the price value (defaults to (0, 0)) 68 | */ 69 | function get(address source, string calldata key) external view returns (uint64, uint64) { 70 | Datum storage datum = data[source][key]; 71 | return (datum.timestamp, datum.value); 72 | } 73 | 74 | /** 75 | * @notice Read only the value for a single key from an authenticated source 76 | * @param source The verifiable author of the data 77 | * @param key The selector for the value to return 78 | * @return The price value (defaults to 0) 79 | */ 80 | function getPrice(address source, string calldata key) external view returns (uint64) { 81 | return data[source][key].value; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Reservoir.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | /** 4 | * @title Reservoir Contract 5 | * @notice Distributes a token to a different contract at a fixed rate. 6 | * @dev This contract must be poked via the `drip()` function every so often. 7 | * @author Channels 8 | */ 9 | contract Reservoir { 10 | 11 | /// @notice The block number when the Reservoir started (immutable) 12 | uint public dripStart; 13 | 14 | /// @notice Tokens per block that to drip to target (immutable) 15 | uint public dripRate; 16 | 17 | /// @notice Reference to token to drip (immutable) 18 | EIP20Interface public token; 19 | 20 | /// @notice Target to receive dripped tokens (immutable) 21 | address public target; 22 | 23 | /// @notice Amount that has already been dripped 24 | uint public dripped; 25 | 26 | /** 27 | * @notice Constructs a Reservoir 28 | * @param dripRate_ Numer of tokens per block to drip 29 | * @param token_ The token to drip 30 | * @param target_ The recipient of dripped tokens 31 | */ 32 | constructor(uint dripRate_, EIP20Interface token_, address target_) public { 33 | dripStart = block.number; 34 | dripRate = dripRate_; 35 | token = token_; 36 | target = target_; 37 | dripped = 0; 38 | } 39 | 40 | /** 41 | * @notice Drips the maximum amount of tokens to match the drip rate since inception 42 | * @dev Note: this will only drip up to the amount of tokens available. 43 | * @return The amount of tokens dripped in this call 44 | */ 45 | function drip() public returns (uint) { 46 | // First, read storage into memory 47 | EIP20Interface token_ = token; 48 | uint reservoirBalance_ = token_.balanceOf(address(this)); // TODO: Verify this is a static call 49 | uint dripRate_ = dripRate; 50 | uint dripStart_ = dripStart; 51 | uint dripped_ = dripped; 52 | address target_ = target; 53 | uint blockNumber_ = block.number; 54 | 55 | // Next, calculate intermediate values 56 | uint dripTotal_ = mul(dripRate_, blockNumber_ - dripStart_, "dripTotal overflow"); 57 | uint deltaDrip_ = sub(dripTotal_, dripped_, "deltaDrip underflow"); 58 | uint toDrip_ = min(reservoirBalance_, deltaDrip_); 59 | uint drippedNext_ = add(dripped_, toDrip_, "tautological"); 60 | 61 | // Finally, write new `dripped` value and transfer tokens to target 62 | dripped = drippedNext_; 63 | token_.transfer(target_, toDrip_); 64 | 65 | return toDrip_; 66 | } 67 | 68 | /* Internal helper functions for safe math */ 69 | 70 | function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 71 | uint c = a + b; 72 | require(c >= a, errorMessage); 73 | return c; 74 | } 75 | 76 | function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 77 | require(b <= a, errorMessage); 78 | uint c = a - b; 79 | return c; 80 | } 81 | 82 | function mul(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 83 | if (a == 0) { 84 | return 0; 85 | } 86 | uint c = a * b; 87 | require(c / a == b, errorMessage); 88 | return c; 89 | } 90 | 91 | function min(uint a, uint b) internal pure returns (uint) { 92 | if (a <= b) { 93 | return a; 94 | } else { 95 | return b; 96 | } 97 | } 98 | } 99 | 100 | import "./EIP20Interface.sol"; 101 | -------------------------------------------------------------------------------- /SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol 4 | // Subject to the MIT license. 5 | 6 | /** 7 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 8 | * checks. 9 | * 10 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 11 | * in bugs, because programmers usually assume that an overflow raises an 12 | * error, which is the standard behavior in high level programming languages. 13 | * `SafeMath` restores this intuition by reverting the transaction when an 14 | * operation overflows. 15 | * 16 | * Using this library instead of the unchecked operations eliminates an entire 17 | * class of bugs, so it's recommended to use it always. 18 | */ 19 | library SafeMath { 20 | /** 21 | * @dev Returns the addition of two unsigned integers, reverting on overflow. 22 | * 23 | * Counterpart to Solidity's `+` operator. 24 | * 25 | * Requirements: 26 | * - Addition cannot overflow. 27 | */ 28 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 29 | uint256 c = a + b; 30 | require(c >= a, "SafeMath: addition overflow"); 31 | 32 | return c; 33 | } 34 | 35 | /** 36 | * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. 37 | * 38 | * Counterpart to Solidity's `+` operator. 39 | * 40 | * Requirements: 41 | * - Addition cannot overflow. 42 | */ 43 | function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 44 | uint256 c = a + b; 45 | require(c >= a, errorMessage); 46 | 47 | return c; 48 | } 49 | 50 | /** 51 | * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). 52 | * 53 | * Counterpart to Solidity's `-` operator. 54 | * 55 | * Requirements: 56 | * - Subtraction cannot underflow. 57 | */ 58 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 59 | return sub(a, b, "SafeMath: subtraction underflow"); 60 | } 61 | 62 | /** 63 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). 64 | * 65 | * Counterpart to Solidity's `-` operator. 66 | * 67 | * Requirements: 68 | * - Subtraction cannot underflow. 69 | */ 70 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 71 | require(b <= a, errorMessage); 72 | uint256 c = a - b; 73 | 74 | return c; 75 | } 76 | 77 | /** 78 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 79 | * 80 | * Counterpart to Solidity's `*` operator. 81 | * 82 | * Requirements: 83 | * - Multiplication cannot overflow. 84 | */ 85 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 86 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 87 | // benefit is lost if 'b' is also tested. 88 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 89 | if (a == 0) { 90 | return 0; 91 | } 92 | 93 | uint256 c = a * b; 94 | require(c / a == b, "SafeMath: multiplication overflow"); 95 | 96 | return c; 97 | } 98 | 99 | /** 100 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 101 | * 102 | * Counterpart to Solidity's `*` operator. 103 | * 104 | * Requirements: 105 | * - Multiplication cannot overflow. 106 | */ 107 | function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 108 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 109 | // benefit is lost if 'b' is also tested. 110 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 111 | if (a == 0) { 112 | return 0; 113 | } 114 | 115 | uint256 c = a * b; 116 | require(c / a == b, errorMessage); 117 | 118 | return c; 119 | } 120 | 121 | /** 122 | * @dev Returns the integer division of two unsigned integers. 123 | * Reverts on division by zero. The result is rounded towards zero. 124 | * 125 | * Counterpart to Solidity's `/` operator. Note: this function uses a 126 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 127 | * uses an invalid opcode to revert (consuming all remaining gas). 128 | * 129 | * Requirements: 130 | * - The divisor cannot be zero. 131 | */ 132 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 133 | return div(a, b, "SafeMath: division by zero"); 134 | } 135 | 136 | /** 137 | * @dev Returns the integer division of two unsigned integers. 138 | * Reverts with custom message on division by zero. The result is rounded towards zero. 139 | * 140 | * Counterpart to Solidity's `/` operator. Note: this function uses a 141 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 142 | * uses an invalid opcode to revert (consuming all remaining gas). 143 | * 144 | * Requirements: 145 | * - The divisor cannot be zero. 146 | */ 147 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 148 | // Solidity only automatically asserts when dividing by 0 149 | require(b > 0, errorMessage); 150 | uint256 c = a / b; 151 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 152 | 153 | return c; 154 | } 155 | 156 | /** 157 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 158 | * Reverts when dividing by zero. 159 | * 160 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 161 | * opcode (which leaves remaining gas untouched) while Solidity uses an 162 | * invalid opcode to revert (consuming all remaining gas). 163 | * 164 | * Requirements: 165 | * - The divisor cannot be zero. 166 | */ 167 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 168 | return mod(a, b, "SafeMath: modulo by zero"); 169 | } 170 | 171 | /** 172 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 173 | * Reverts with custom message when dividing by zero. 174 | * 175 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 176 | * opcode (which leaves remaining gas untouched) while Solidity uses an 177 | * invalid opcode to revert (consuming all remaining gas). 178 | * 179 | * Requirements: 180 | * - The divisor cannot be zero. 181 | */ 182 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 183 | require(b != 0, errorMessage); 184 | return a % b; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Unitroller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./ErrorReporter.sol"; 4 | import "./ComptrollerStorage.sol"; 5 | /** 6 | * @title ComptrollerCore 7 | * @dev Storage for the comptroller is at this address, while execution is delegated to the `comptrollerImplementation`. 8 | * CTokens should reference this contract as their comptroller. 9 | */ 10 | contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { 11 | 12 | /** 13 | * @notice Emitted when pendingComptrollerImplementation is changed 14 | */ 15 | event NewPendingImplementation(address oldPendingImplementation, address newPendingImplementation); 16 | 17 | /** 18 | * @notice Emitted when pendingComptrollerImplementation is accepted, which means comptroller implementation is updated 19 | */ 20 | event NewImplementation(address oldImplementation, address newImplementation); 21 | 22 | /** 23 | * @notice Emitted when pendingAdmin is changed 24 | */ 25 | event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); 26 | 27 | /** 28 | * @notice Emitted when pendingAdmin is accepted, which means admin is updated 29 | */ 30 | event NewAdmin(address oldAdmin, address newAdmin); 31 | 32 | constructor() public { 33 | // Set admin to caller 34 | admin = msg.sender; 35 | } 36 | 37 | /*** Admin Functions ***/ 38 | function _setPendingImplementation(address newPendingImplementation) public returns (uint) { 39 | 40 | if (msg.sender != admin) { 41 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK); 42 | } 43 | 44 | address oldPendingImplementation = pendingComptrollerImplementation; 45 | 46 | pendingComptrollerImplementation = newPendingImplementation; 47 | 48 | emit NewPendingImplementation(oldPendingImplementation, pendingComptrollerImplementation); 49 | 50 | return uint(Error.NO_ERROR); 51 | } 52 | 53 | /** 54 | * @notice Accepts new implementation of comptroller. msg.sender must be pendingImplementation 55 | * @dev Admin function for new implementation to accept it's role as implementation 56 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 57 | */ 58 | function _acceptImplementation() public returns (uint) { 59 | // Check caller is pendingImplementation and pendingImplementation ≠ address(0) 60 | if (msg.sender != pendingComptrollerImplementation || pendingComptrollerImplementation == address(0)) { 61 | return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK); 62 | } 63 | 64 | // Save current values for inclusion in log 65 | address oldImplementation = comptrollerImplementation; 66 | address oldPendingImplementation = pendingComptrollerImplementation; 67 | 68 | comptrollerImplementation = pendingComptrollerImplementation; 69 | 70 | pendingComptrollerImplementation = address(0); 71 | 72 | emit NewImplementation(oldImplementation, comptrollerImplementation); 73 | emit NewPendingImplementation(oldPendingImplementation, pendingComptrollerImplementation); 74 | 75 | return uint(Error.NO_ERROR); 76 | } 77 | 78 | 79 | /** 80 | * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. 81 | * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. 82 | * @param newPendingAdmin New pending admin. 83 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 84 | */ 85 | function _setPendingAdmin(address newPendingAdmin) public returns (uint) { 86 | // Check caller = admin 87 | if (msg.sender != admin) { 88 | return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK); 89 | } 90 | 91 | // Save current value, if any, for inclusion in log 92 | address oldPendingAdmin = pendingAdmin; 93 | 94 | // Store pendingAdmin with value newPendingAdmin 95 | pendingAdmin = newPendingAdmin; 96 | 97 | // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) 98 | emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); 99 | 100 | return uint(Error.NO_ERROR); 101 | } 102 | 103 | /** 104 | * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin 105 | * @dev Admin function for pending admin to accept role and update admin 106 | * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) 107 | */ 108 | function _acceptAdmin() public returns (uint) { 109 | // Check caller is pendingAdmin and pendingAdmin ≠ address(0) 110 | if (msg.sender != pendingAdmin || msg.sender == address(0)) { 111 | return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK); 112 | } 113 | 114 | // Save current values for inclusion in log 115 | address oldAdmin = admin; 116 | address oldPendingAdmin = pendingAdmin; 117 | 118 | // Store admin with value pendingAdmin 119 | admin = pendingAdmin; 120 | 121 | // Clear the pending value 122 | pendingAdmin = address(0); 123 | 124 | emit NewAdmin(oldAdmin, admin); 125 | emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); 126 | 127 | return uint(Error.NO_ERROR); 128 | } 129 | 130 | /** 131 | * @dev Delegates execution to an implementation contract. 132 | * It returns to the external caller whatever the implementation returns 133 | * or forwards reverts. 134 | */ 135 | function () payable external { 136 | // delegate all other functions to current implementation 137 | (bool success, ) = comptrollerImplementation.delegatecall(msg.data); 138 | 139 | assembly { 140 | let free_mem_ptr := mload(0x40) 141 | returndatacopy(free_mem_ptr, 0, returndatasize) 142 | 143 | switch success 144 | case 0 { revert(free_mem_ptr, returndatasize) } 145 | default { return(free_mem_ptr, returndatasize) } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /WhitePaperInterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | import "./InterestRateModel.sol"; 4 | import "./SafeMath.sol"; 5 | 6 | /** 7 | * @title Channels's WhitePaperInterestRateModel Contract 8 | * @author Channels 9 | * @notice The parameterized model described in section 2.4 of the original Channels Protocol whitepaper 10 | */ 11 | contract WhitePaperInterestRateModel is InterestRateModel { 12 | using SafeMath for uint; 13 | 14 | event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock); 15 | 16 | /** 17 | * @notice The approximate number of blocks per year that is assumed by the interest rate model 18 | */ 19 | uint public constant blocksPerYear = 10512000; 20 | 21 | /** 22 | * @notice The multiplier of utilization rate that gives the slope of the interest rate 23 | */ 24 | uint public multiplierPerBlock; 25 | 26 | /** 27 | * @notice The base interest rate which is the y-intercept when utilization rate is 0 28 | */ 29 | uint public baseRatePerBlock; 30 | 31 | /** 32 | * @notice Construct an interest rate model 33 | * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) 34 | * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) 35 | */ 36 | constructor(uint baseRatePerYear, uint multiplierPerYear) public { 37 | baseRatePerBlock = baseRatePerYear.div(blocksPerYear); 38 | multiplierPerBlock = multiplierPerYear.div(blocksPerYear); 39 | 40 | emit NewInterestParams(baseRatePerBlock, multiplierPerBlock); 41 | } 42 | 43 | /** 44 | * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)` 45 | * @param cash The amount of cash in the market 46 | * @param borrows The amount of borrows in the market 47 | * @param reserves The amount of reserves in the market (currently unused) 48 | * @return The utilization rate as a mantissa between [0, 1e18] 49 | */ 50 | function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) { 51 | // Utilization rate is 0 when there are no borrows 52 | if (borrows == 0) { 53 | return 0; 54 | } 55 | 56 | return borrows.mul(1e18).div(cash.add(borrows).sub(reserves)); 57 | } 58 | 59 | /** 60 | * @notice Calculates the current borrow rate per block, with the error code expected by the market 61 | * @param cash The amount of cash in the market 62 | * @param borrows The amount of borrows in the market 63 | * @param reserves The amount of reserves in the market 64 | * @return The borrow rate percentage per block as a mantissa (scaled by 1e18) 65 | */ 66 | function getBorrowRate(uint cash, uint borrows, uint reserves) public view returns (uint) { 67 | uint ur = utilizationRate(cash, borrows, reserves); 68 | return ur.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock); 69 | } 70 | 71 | /** 72 | * @notice Calculates the current supply rate per block 73 | * @param cash The amount of cash in the market 74 | * @param borrows The amount of borrows in the market 75 | * @param reserves The amount of reserves in the market 76 | * @param reserveFactorMantissa The current reserve factor for the market 77 | * @return The supply rate percentage per block as a mantissa (scaled by 1e18) 78 | */ 79 | function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) { 80 | uint oneMinusReserveFactor = uint(1e18).sub(reserveFactorMantissa); 81 | uint borrowRate = getBorrowRate(cash, borrows, reserves); 82 | uint rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18); 83 | return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18); 84 | } 85 | } 86 | --------------------------------------------------------------------------------