Comptroller

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/Comptroller.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause\r
pragma solidity ^0.8.10;\r
\r
import "./CToken.sol";\r
import "./ErrorReporter.sol";\r
import "./PriceOracle.sol";\r
import "./ComptrollerInterface.sol";\r
import "./ComptrollerStorage.sol";\r
import "./Unitroller.sol";\r
\r
// 简化的 Comp 接口,用于替换 import "./Governance/Comp.sol"\r
interface Comp {\r
    function balanceOf(address account) external view returns (uint);\r
    function transfer(address to, uint amount) external returns (bool);\r
}\r
\r
/**\r
 * @title Compound's Comptroller Contract\r
 * @author Compound\r
 */\r
contract Comptroller is ComptrollerV7Storage, ComptrollerInterface, ComptrollerErrorReporter, ExponentialNoError {\r
    /// @notice Emitted when an admin supports a market\r
    event MarketListed(CToken cToken);\r
\r
    /// @notice Emitted when an account enters a market\r
    event MarketEntered(CToken cToken, address account);\r
\r
    /// @notice Emitted when an account exits a market\r
    event MarketExited(CToken cToken, address account);\r
\r
    /// @notice Emitted when close factor is changed by admin\r
    event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa);\r
\r
    /// @notice Emitted when a collateral factor is changed by admin\r
    event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa);\r
\r
    /// @notice Emitted when liquidation incentive is changed by admin\r
    event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa);\r
\r
    /// @notice Emitted when price oracle is changed\r
    event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle);\r
\r
    /// @notice Emitted when pause guardian is changed\r
    event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian);\r
\r
    /// @notice Emitted when an action is paused globally\r
    event ActionPaused(string action, bool pauseState);\r
\r
    /// @notice Emitted when an action is paused on a market\r
    event ActionPaused(CToken cToken, string action, bool pauseState);\r
\r
    /// @notice Emitted when a new borrow-side COMP speed is calculated for a market\r
    event CompBorrowSpeedUpdated(CToken indexed cToken, uint newSpeed);\r
\r
    /// @notice Emitted when a new supply-side COMP speed is calculated for a market\r
    event CompSupplySpeedUpdated(CToken indexed cToken, uint newSpeed);\r
\r
    /// @notice Emitted when a new COMP speed is set for a contributor\r
    event ContributorCompSpeedUpdated(address indexed contributor, uint newSpeed);\r
\r
    /// @notice Emitted when COMP is distributed to a supplier\r
    event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex);\r
\r
    /// @notice Emitted when COMP is distributed to a borrower\r
    event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex);\r
\r
    /// @notice Emitted when borrow cap for a cToken is changed\r
    event NewBorrowCap(CToken indexed cToken, uint newBorrowCap);\r
\r
    /// @notice Emitted when borrow cap guardian is changed\r
    event NewBorrowCapGuardian(address oldBorrowCapGuardian, address newBorrowCapGuardian);\r
\r
    /// @notice Emitted when COMP is granted by admin\r
    event CompGranted(address recipient, uint amount);\r
\r
    /// @notice Emitted when COMP accrued for a user has been manually adjusted.\r
    event CompAccruedAdjusted(address indexed user, uint oldCompAccrued, uint newCompAccrued);\r
\r
    /// @notice Emitted when COMP receivable for a user has been updated.\r
    event CompReceivableUpdated(address indexed user, uint oldCompReceivable, uint newCompReceivable);\r
\r
    /// @notice The initial COMP index for a market\r
    uint224 public constant compInitialIndex = 1e36;\r
\r
    // closeFactorMantissa must be strictly greater than this value\r
    uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05\r
\r
    // closeFactorMantissa must not exceed this value\r
    uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9\r
\r
    // No collateralFactorMantissa may exceed this value\r
    uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9\r
\r
    constructor() {\r
        admin = msg.sender;\r
    }\r
\r
    /*** Assets You Are In ***/\r
\r
    /**\r
     * @notice Returns the assets an account has entered\r
     * @param account The address of the account to pull assets for\r
     * @return A dynamic list with the assets the account has entered\r
     */\r
    function getAssetsIn(address account) external view returns (CToken[] memory) {\r
        CToken[] memory assetsIn = accountAssets[account];\r
\r
        return assetsIn;\r
    }\r
\r
    /**\r
     * @notice Returns whether the given account is entered in the given asset\r
     * @param account The address of the account to check\r
     * @param cToken The cToken to check\r
     * @return True if the account is in the asset, otherwise false.\r
     */\r
    function checkMembership(address account, CToken cToken) external view returns (bool) {\r
        return markets[address(cToken)].accountMembership[account];\r
    }\r
\r
    /**\r
     * @notice Add assets to be included in account liquidity calculation\r
     * @param cTokens The list of addresses of the cToken markets to be enabled\r
     * @return Success indicator for whether each corresponding market was entered\r
     */\r
    function enterMarkets(address[] memory cTokens) override public returns (uint[] memory) {\r
        uint len = cTokens.length;\r
\r
        uint[] memory results = new uint[](len);\r
        for (uint i = 0; i < len; i++) {\r
            CToken cToken = CToken(cTokens[i]);\r
\r
            results[i] = uint(addToMarketInternal(cToken, msg.sender));\r
        }\r
\r
        return results;\r
    }\r
\r
    /**\r
     * @notice Add the market to the borrower's "assets in" for liquidity calculations\r
     * @param cToken The market to enter\r
     * @param borrower The address of the account to modify\r
     * @return Success indicator for whether the market was entered\r
     */\r
    function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) {\r
        Market storage marketToJoin = markets[address(cToken)];\r
\r
        if (!marketToJoin.isListed) {\r
            // market is not listed, cannot join\r
            return Error.MARKET_NOT_LISTED;\r
        }\r
\r
        if (marketToJoin.accountMembership[borrower] == true) {\r
            // already joined\r
            return Error.NO_ERROR;\r
        }\r
\r
        // survived the gauntlet, add to list\r
        // NOTE: we store these somewhat redundantly as a significant optimization\r
        //  this avoids having to iterate through the list for the most common use cases\r
        //  that is, only when we need to perform liquidity checks\r
        //  and not whenever we want to check if an account is in a particular market\r
        marketToJoin.accountMembership[borrower] = true;\r
        accountAssets[borrower].push(cToken);\r
\r
        emit MarketEntered(cToken, borrower);\r
\r
        return Error.NO_ERROR;\r
    }\r
\r
    /**\r
     * @notice Removes asset from sender's account liquidity calculation\r
     * @dev Sender must not have an outstanding borrow balance in the asset,\r
     *  or be providing necessary collateral for an outstanding borrow.\r
     * @param cTokenAddress The address of the asset to be removed\r
     * @return Whether or not the account successfully exited the market\r
     */\r
    function exitMarket(address cTokenAddress) override external returns (uint) {\r
        CToken cToken = CToken(cTokenAddress);\r
        /* Get sender tokensHeld and amountOwed underlying from the cToken */\r
        (uint oErr, uint tokensHeld, uint amountOwed, ) = cToken.getAccountSnapshot(msg.sender);\r
        require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code\r
\r
        /* Fail if the sender has a borrow balance */\r
        if (amountOwed != 0) {\r
            return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED);\r
        }\r
\r
        /* Fail if the sender is not permitted to redeem all of their tokens */\r
        uint allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld);\r
        if (allowed != 0) {\r
            return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed);\r
        }\r
\r
        Market storage marketToExit = markets[address(cToken)];\r
\r
        /* Return true if the sender is not already ‘in’ the market */\r
        if (!marketToExit.accountMembership[msg.sender]) {\r
            return uint(Error.NO_ERROR);\r
        }\r
\r
        /* Set cToken account membership to false */\r
        delete marketToExit.accountMembership[msg.sender];\r
\r
        /* Delete cToken from the account’s list of assets */\r
        // load into memory for faster iteration\r
        CToken[] memory userAssetList = accountAssets[msg.sender];\r
        uint len = userAssetList.length;\r
        uint assetIndex = len;\r
        for (uint i = 0; i < len; i++) {\r
            if (userAssetList[i] == cToken) {\r
                assetIndex = i;\r
                break;\r
            }\r
        }\r
\r
        // We *must* have found the asset in the list or our redundant data structure is broken\r
        assert(assetIndex < len);\r
\r
        // copy last item in list to location of item to be removed, reduce length by 1\r
        CToken[] storage storedList = accountAssets[msg.sender];\r
        storedList[assetIndex] = storedList[storedList.length - 1];\r
        storedList.pop();\r
\r
        emit MarketExited(cToken, msg.sender);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /*** Policy Hooks ***/\r
\r
    /**\r
     * @notice Checks if the account should be allowed to mint tokens in the given market\r
     * @param cToken The market to verify the mint against\r
     * @param minter The account which would get the minted tokens\r
     * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\r
     * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)\r
     */\r
    function mintAllowed(address cToken, address minter, uint mintAmount) override external returns (uint) {\r
        // Pausing is a very serious situation - we revert to sound the alarms\r
        require(!mintGuardianPaused[cToken], "mint is paused");\r
\r
        // Shh - currently unused\r
        minter;\r
        mintAmount;\r
\r
        if (!markets[cToken].isListed) {\r
            return uint(Error.MARKET_NOT_LISTED);\r
        }\r
\r
        // Keep the flywheel moving\r
        updateCompSupplyIndex(cToken);\r
        distributeSupplierComp(cToken, minter);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates mint and reverts on rejection. May emit logs.\r
     * @param cToken Asset being minted\r
     * @param minter The address minting the tokens\r
     * @param actualMintAmount The amount of the underlying asset being minted\r
     * @param mintTokens The number of tokens being minted\r
     */\r
    function mintVerify(address cToken, address minter, uint actualMintAmount, uint mintTokens) override external {\r
        // Shh - currently unused\r
        cToken;\r
        minter;\r
        actualMintAmount;\r
        mintTokens;\r
\r
        // Shh - we don't ever want this hook to be marked pure\r
        if (false) {\r
            maxAssets = maxAssets;\r
        }\r
    }\r
\r
    /**\r
     * @notice Checks if the account should be allowed to redeem tokens in the given market\r
     * @param cToken The market to verify the redeem against\r
     * @param redeemer The account which would redeem the tokens\r
     * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market\r
     * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)\r
     */\r
    function redeemAllowed(address cToken, address redeemer, uint redeemTokens) override external returns (uint) {\r
        uint allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens);\r
        if (allowed != uint(Error.NO_ERROR)) {\r
            return allowed;\r
        }\r
\r
        // Keep the flywheel moving\r
        updateCompSupplyIndex(cToken);\r
        distributeSupplierComp(cToken, redeemer);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    function redeemAllowedInternal(address cToken, address redeemer, uint redeemTokens) internal view returns (uint) {\r
        if (!markets[cToken].isListed) {\r
            return uint(Error.MARKET_NOT_LISTED);\r
        }\r
\r
        /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\r
        if (!markets[cToken].accountMembership[redeemer]) {\r
            return uint(Error.NO_ERROR);\r
        }\r
\r
        /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\r
        (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, CToken(cToken), redeemTokens, 0);\r
        if (err != Error.NO_ERROR) {\r
            return uint(err);\r
        }\r
        if (shortfall > 0) {\r
            return uint(Error.INSUFFICIENT_LIQUIDITY);\r
        }\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates redeem and reverts on rejection. May emit logs.\r
     * @param cToken Asset being redeemed\r
     * @param redeemer The address redeeming the tokens\r
     * @param redeemAmount The amount of the underlying asset being redeemed\r
     * @param redeemTokens The number of tokens being redeemed\r
     */\r
    function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) override external {\r
        // Shh - currently unused\r
        cToken;\r
        redeemer;\r
\r
        // Require tokens is zero or amount is also zero\r
        if (redeemTokens == 0 && redeemAmount > 0) {\r
            revert("redeemTokens zero");\r
        }\r
    }\r
\r
    /**\r
     * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\r
     * @param cToken The market to verify the borrow against\r
     * @param borrower The account which would borrow the asset\r
     * @param borrowAmount The amount of underlying the account would borrow\r
     * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)\r
     */\r
    function borrowAllowed(address cToken, address borrower, uint borrowAmount) override external returns (uint) {\r
        // Pausing is a very serious situation - we revert to sound the alarms\r
        require(!borrowGuardianPaused[cToken], "borrow is paused");\r
\r
        if (!markets[cToken].isListed) {\r
            return uint(Error.MARKET_NOT_LISTED);\r
        }\r
\r
        if (!markets[cToken].accountMembership[borrower]) {\r
            // only cTokens may call borrowAllowed if borrower not in market\r
            require(msg.sender == cToken, "sender must be cToken");\r
\r
            // attempt to add borrower to the market\r
            Error err = addToMarketInternal(CToken(msg.sender), borrower);\r
            if (err != Error.NO_ERROR) {\r
                return uint(err);\r
            }\r
\r
            // it should be impossible to break the important invariant\r
            assert(markets[cToken].accountMembership[borrower]);\r
        }\r
\r
        if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) {\r
            return uint(Error.PRICE_ERROR);\r
        }\r
\r
\r
        uint borrowCap = borrowCaps[cToken];\r
        // Borrow cap of 0 corresponds to unlimited borrowing\r
        if (borrowCap != 0) {\r
            uint totalBorrows = CToken(cToken).totalBorrows();\r
            uint nextTotalBorrows = add_(totalBorrows, borrowAmount);\r
            require(nextTotalBorrows < borrowCap, "market borrow cap reached");\r
        }\r
\r
        (Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, CToken(cToken), 0, borrowAmount);\r
        if (err != Error.NO_ERROR) {\r
            return uint(err);\r
        }\r
        if (shortfall > 0) {\r
            return uint(Error.INSUFFICIENT_LIQUIDITY);\r
        }\r
\r
        // Keep the flywheel moving\r
        Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()});\r
        updateCompBorrowIndex(cToken, borrowIndex);\r
        distributeBorrowerComp(cToken, borrower, borrowIndex);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates borrow and reverts on rejection. May emit logs.\r
     * @param cToken Asset whose underlying is being borrowed\r
     * @param borrower The address borrowing the underlying\r
     * @param borrowAmount The amount of the underlying asset requested to borrow\r
     */\r
    function borrowVerify(address cToken, address borrower, uint borrowAmount) override external {\r
        // Shh - currently unused\r
        cToken;\r
        borrower;\r
        borrowAmount;\r
\r
        // Shh - we don't ever want this hook to be marked pure\r
        if (false) {\r
            maxAssets = maxAssets;\r
        }\r
    }\r
\r
    /**\r
     * @notice Checks if the account should be allowed to repay a borrow in the given market\r
     * @param cToken The market to verify the repay against\r
     * @param payer The account which would repay the asset\r
     * @param borrower The account which would borrowed the asset\r
     * @param repayAmount The amount of the underlying asset the account would repay\r
     * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)\r
     */\r
    function repayBorrowAllowed(\r
        address cToken,\r
        address payer,\r
        address borrower,\r
        uint repayAmount) override external returns (uint) {\r
        // Shh - currently unused\r
        payer;\r
        borrower;\r
        repayAmount;\r
\r
        if (!markets[cToken].isListed) {\r
            return uint(Error.MARKET_NOT_LISTED);\r
        }\r
\r
        // Keep the flywheel moving\r
        Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()});\r
        updateCompBorrowIndex(cToken, borrowIndex);\r
        distributeBorrowerComp(cToken, borrower, borrowIndex);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates repayBorrow and reverts on rejection. May emit logs.\r
     * @param cToken Asset being repaid\r
     * @param payer The address repaying the borrow\r
     * @param borrower The address of the borrower\r
     * @param actualRepayAmount The amount of underlying being repaid\r
     */\r
    function repayBorrowVerify(\r
        address cToken,\r
        address payer,\r
        address borrower,\r
        uint actualRepayAmount,\r
        uint borrowerIndex) override external {\r
        // Shh - currently unused\r
        cToken;\r
        payer;\r
        borrower;\r
        actualRepayAmount;\r
        borrowerIndex;\r
\r
        // Shh - we don't ever want this hook to be marked pure\r
        if (false) {\r
            maxAssets = maxAssets;\r
        }\r
    }\r
\r
    /**\r
     * @notice Checks if the liquidation should be allowed to occur\r
     * @param cTokenBorrowed Asset which was borrowed by the borrower\r
     * @param cTokenCollateral Asset which was used as collateral and will be seized\r
     * @param liquidator The address repaying the borrow and seizing the collateral\r
     * @param borrower The address of the borrower\r
     * @param repayAmount The amount of underlying being repaid\r
     */\r
    function liquidateBorrowAllowed(\r
        address cTokenBorrowed,\r
        address cTokenCollateral,\r
        address liquidator,\r
        address borrower,\r
        uint repayAmount) override external returns (uint) {\r
        // Shh - currently unused\r
        liquidator;\r
\r
        if (!markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed) {\r
            return uint(Error.MARKET_NOT_LISTED);\r
        }\r
\r
        uint borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower);\r
\r
        /* allow accounts to be liquidated if the market is deprecated */\r
        if (isDeprecated(CToken(cTokenBorrowed))) {\r
            require(borrowBalance >= repayAmount, "Can not repay more than the total borrow");\r
        } else {\r
            /* The borrower must have shortfall in order to be liquidatable */\r
            (Error err, , uint shortfall) = getAccountLiquidityInternal(borrower);\r
            if (err != Error.NO_ERROR) {\r
                return uint(err);\r
            }\r
\r
            if (shortfall == 0) {\r
                return uint(Error.INSUFFICIENT_SHORTFALL);\r
            }\r
\r
            /* The liquidator may not repay more than what is allowed by the closeFactor */\r
            uint maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance);\r
            if (repayAmount > maxClose) {\r
                return uint(Error.TOO_MUCH_REPAY);\r
            }\r
        }\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates liquidateBorrow and reverts on rejection. May emit logs.\r
     * @param cTokenBorrowed Asset which was borrowed by the borrower\r
     * @param cTokenCollateral Asset which was used as collateral and will be seized\r
     * @param liquidator The address repaying the borrow and seizing the collateral\r
     * @param borrower The address of the borrower\r
     * @param actualRepayAmount The amount of underlying being repaid\r
     */\r
    function liquidateBorrowVerify(\r
        address cTokenBorrowed,\r
        address cTokenCollateral,\r
        address liquidator,\r
        address borrower,\r
        uint actualRepayAmount,\r
        uint seizeTokens) override external {\r
        // Shh - currently unused\r
        cTokenBorrowed;\r
        cTokenCollateral;\r
        liquidator;\r
        borrower;\r
        actualRepayAmount;\r
        seizeTokens;\r
\r
        // Shh - we don't ever want this hook to be marked pure\r
        if (false) {\r
            maxAssets = maxAssets;\r
        }\r
    }\r
\r
    /**\r
     * @notice Checks if the seizing of assets should be allowed to occur\r
     * @param cTokenCollateral Asset which was used as collateral and will be seized\r
     * @param cTokenBorrowed Asset which was borrowed by the borrower\r
     * @param liquidator The address repaying the borrow and seizing the collateral\r
     * @param borrower The address of the borrower\r
     * @param seizeTokens The number of collateral tokens to seize\r
     */\r
    function seizeAllowed(\r
        address cTokenCollateral,\r
        address cTokenBorrowed,\r
        address liquidator,\r
        address borrower,\r
        uint seizeTokens) override external returns (uint) {\r
        // Pausing is a very serious situation - we revert to sound the alarms\r
        require(!seizeGuardianPaused, "seize is paused");\r
\r
        // Shh - currently unused\r
        seizeTokens;\r
\r
        if (!markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed) {\r
            return uint(Error.MARKET_NOT_LISTED);\r
        }\r
\r
        if (CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller()) {\r
            return uint(Error.COMPTROLLER_MISMATCH);\r
        }\r
\r
        // Keep the flywheel moving\r
        updateCompSupplyIndex(cTokenCollateral);\r
        distributeSupplierComp(cTokenCollateral, borrower);\r
        distributeSupplierComp(cTokenCollateral, liquidator);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates seize and reverts on rejection. May emit logs.\r
     * @param cTokenCollateral Asset which was used as collateral and will be seized\r
     * @param cTokenBorrowed Asset which was borrowed by the borrower\r
     * @param liquidator The address repaying the borrow and seizing the collateral\r
     * @param borrower The address of the borrower\r
     * @param seizeTokens The number of collateral tokens to seize\r
     */\r
    function seizeVerify(\r
        address cTokenCollateral,\r
        address cTokenBorrowed,\r
        address liquidator,\r
        address borrower,\r
        uint seizeTokens) override external {\r
        // Shh - currently unused\r
        cTokenCollateral;\r
        cTokenBorrowed;\r
        liquidator;\r
        borrower;\r
        seizeTokens;\r
\r
        // Shh - we don't ever want this hook to be marked pure\r
        if (false) {\r
            maxAssets = maxAssets;\r
        }\r
    }\r
\r
    /**\r
     * @notice Checks if the account should be allowed to transfer tokens in the given market\r
     * @param cToken The market to verify the transfer against\r
     * @param src The account which sources the tokens\r
     * @param dst The account which receives the tokens\r
     * @param transferTokens The number of cTokens to transfer\r
     * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)\r
     */\r
    function transferAllowed(address cToken, address src, address dst, uint transferTokens) override external returns (uint) {\r
        // Pausing is a very serious situation - we revert to sound the alarms\r
        require(!transferGuardianPaused, "transfer is paused");\r
\r
        // Currently the only consideration is whether or not\r
        //  the src is allowed to redeem this many tokens\r
        uint allowed = redeemAllowedInternal(cToken, src, transferTokens);\r
        if (allowed != uint(Error.NO_ERROR)) {\r
            return allowed;\r
        }\r
\r
        // Keep the flywheel moving\r
        updateCompSupplyIndex(cToken);\r
        distributeSupplierComp(cToken, src);\r
        distributeSupplierComp(cToken, dst);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
     * @notice Validates transfer and reverts on rejection. May emit logs.\r
     * @param cToken Asset being transferred\r
     * @param src The account which sources the tokens\r
     * @param dst The account which receives the tokens\r
     * @param transferTokens The number of cTokens to transfer\r
     */\r
    function transferVerify(address cToken, address src, address dst, uint transferTokens) override external {\r
        // Shh - currently unused\r
        cToken;\r
        src;\r
        dst;\r
        transferTokens;\r
\r
        // Shh - we don't ever want this hook to be marked pure\r
        if (false) {\r
            maxAssets = maxAssets;\r
        }\r
    }\r
\r
    /*** Liquidity/Liquidation Calculations ***/\r
\r
    /**\r
     * @dev Local vars for avoiding stack-depth limits in calculating account liquidity.\r
     *  Note that `cTokenBalance` is the number of cTokens the account owns in the market,\r
     *  whereas `borrowBalance` is the amount of underlying that the account has borrowed.\r
     */\r
    struct AccountLiquidityLocalVars {\r
        uint sumCollateral;\r
        uint sumBorrowPlusEffects;\r
        uint cTokenBalance;\r
        uint borrowBalance;\r
        uint exchangeRateMantissa;\r
        uint oraclePriceMantissa;\r
        Exp collateralFactor;\r
        Exp exchangeRate;\r
        Exp oraclePrice;\r
        Exp tokensToDenom;\r
    }\r
\r
    /**\r
     * @notice Determine the current account liquidity wrt collateral requirements\r
     * @return (possible error code (semi-opaque),\r
                account liquidity in excess of collateral requirements,\r
     *          account shortfall below collateral requirements)\r
     */\r
    function getAccountLiquidity(address account) public view returns (uint, uint, uint) {\r
        (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(address(0)), 0, 0);\r
\r
        return (uint(err), liquidity, shortfall);\r
    }\r
\r
    /**\r
     * @notice Determine the current account liquidity wrt collateral requirements\r
     * @return (possible error code,\r
                account liquidity in excess of collateral requirements,\r
     *          account shortfall below collateral requirements)\r
     */\r
    function getAccountLiquidityInternal(address account) internal view returns (Error, uint, uint) {\r
        return getHypotheticalAccountLiquidityInternal(account, CToken(address(0)), 0, 0);\r
    }\r
\r
    /**\r
     * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\r
     * @param cTokenModify The market to hypothetically redeem/borrow in\r
     * @param account The account to determine liquidity for\r
     * @param redeemTokens The number of tokens to hypothetically redeem\r
     * @param borrowAmount The amount of underlying to hypothetically borrow\r
     * @return (possible error code (semi-opaque),\r
                hypothetical account liquidity in excess of collateral requirements,\r
     *          hypothetical account shortfall below collateral requirements)\r
     */\r
    function getHypotheticalAccountLiquidity(\r
        address account,\r
        address cTokenModify,\r
        uint redeemTokens,\r
        uint borrowAmount) public view returns (uint, uint, uint) {\r
        (Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount);\r
        return (uint(err), liquidity, shortfall);\r
    }\r
\r
    /**\r
     * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\r
     * @param cTokenModify The market to hypothetically redeem/borrow in\r
     * @param account The account to determine liquidity for\r
     * @param redeemTokens The number of tokens to hypothetically redeem\r
     * @param borrowAmount The amount of underlying to hypothetically borrow\r
     * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data,\r
     *  without calculating accumulated interest.\r
     * @return (possible error code,\r
                hypothetical account liquidity in excess of collateral requirements,\r
     *          hypothetical account shortfall below collateral requirements)\r
     */\r
    function getHypotheticalAccountLiquidityInternal(\r
        address account,\r
        CToken cTokenModify,\r
        uint redeemTokens,\r
        uint borrowAmount) internal view returns (Error, uint, uint) {\r
\r
        AccountLiquidityLocalVars memory vars; // Holds all our calculation results\r
        uint oErr;\r
\r
        // For each asset the account is in\r
        CToken[] memory assets = accountAssets[account];\r
        for (uint i = 0; i < assets.length; i++) {\r
            CToken asset = assets[i];\r
\r
            // Read the balances and exchange rate from the cToken\r
            (oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account);\r
            if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades\r
                return (Error.SNAPSHOT_ERROR, 0, 0);\r
            }\r
            vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa});\r
            vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa});\r
\r
            // Get the normalized price of the asset\r
            vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset);\r
            if (vars.oraclePriceMantissa == 0) {\r
                return (Error.PRICE_ERROR, 0, 0);\r
            }\r
            vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa});\r
\r
            // Pre-compute a conversion factor from tokens -> ether (normalized price value)\r
            vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice);\r
\r
            // sumCollateral += tokensToDenom * cTokenBalance\r
            vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral);\r
\r
            // sumBorrowPlusEffects += oraclePrice * borrowBalance\r
            vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects);\r
\r
            // Calculate effects of interacting with cTokenModify\r
            if (asset == cTokenModify) {\r
                // redeem effect\r
                // sumBorrowPlusEffects += tokensToDenom * redeemTokens\r
                vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects);\r
\r
                // borrow effect\r
                // sumBorrowPlusEffects += oraclePrice * borrowAmount\r
                vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects);\r
            }\r
        }\r
\r
        // These are safe, as the underflow condition is checked first\r
        if (vars.sumCollateral > vars.sumBorrowPlusEffects) {\r
            return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0);\r
        } else {\r
            return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral);\r
        }\r
    }\r
\r
    /**\r
     * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\r
     * @dev Used in liquidation (called in cToken.liquidateBorrowFresh)\r
     * @param cTokenBorrowed The address of the borrowed cToken\r
     * @param cTokenCollateral The address of the collateral cToken\r
     * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens\r
     * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation)\r
     */\r
    function liquidateCalculateSeizeTokens(address cTokenBorrowed, address cTokenCollateral, uint actualRepayAmount) override external view returns (uint, uint) {\r
        /* Read oracle prices for borrowed and collateral markets */\r
        uint priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed));\r
        uint priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral));\r
        if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) {\r
            return (uint(Error.PRICE_ERROR), 0);\r
        }\r
\r
        /*\r
         * Get the exchange rate and calculate the number of collateral tokens to seize:\r
         *  seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\r
         *  seizeTokens = seizeAmount / exchangeRate\r
         *   = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\r
         */\r
        uint exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error\r
        uint seizeTokens;\r
        Exp memory numerator;\r
        Exp memory denominator;\r
        Exp memory ratio;\r
\r
        numerator = mul_(Exp({mantissa: liquidationIncentiveMantissa}), Exp({mantissa: priceBorrowedMantissa}));\r
        denominator = mul_(Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa}));\r
        ratio = div_(numerator, denominator);\r
\r
        seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\r
\r
        return (uint(Error.NO_ERROR), seizeTokens);\r
    }\r
\r
    /*** Admin Functions ***/\r
\r
    /**\r
      * @notice Sets a new price oracle for the comptroller\r
      * @dev Admin function to set a new price oracle\r
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)\r
      */\r
    function _setPriceOracle(PriceOracle newOracle) public returns (uint) {\r
        // Check caller is admin\r
        if (msg.sender != admin) {\r
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK);\r
        }\r
\r
        // Track the old oracle for the comptroller\r
        PriceOracle oldOracle = oracle;\r
\r
        // Set comptroller's oracle to newOracle\r
        oracle = newOracle;\r
\r
        // Emit NewPriceOracle(oldOracle, newOracle)\r
        emit NewPriceOracle(oldOracle, newOracle);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
      * @notice Sets the closeFactor used when liquidating borrows\r
      * @dev Admin function to set closeFactor\r
      * @param newCloseFactorMantissa New close factor, scaled by 1e18\r
      * @return uint 0=success, otherwise a failure\r
      */\r
    function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) {\r
        // Check caller is admin\r
    	require(msg.sender == admin, "only admin can set close factor");\r
\r
        uint oldCloseFactorMantissa = closeFactorMantissa;\r
        closeFactorMantissa = newCloseFactorMantissa;\r
        emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
      * @notice Sets the collateralFactor for a market\r
      * @dev Admin function to set per-market collateralFactor\r
      * @param cToken The market to set the factor on\r
      * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\r
      * @return uint 0=success, otherwise a failure. (See ErrorReporter for details)\r
      */\r
    function _setCollateralFactor(CToken cToken, uint newCollateralFactorMantissa) external returns (uint) {\r
        // Check caller is admin\r
        if (msg.sender != admin) {\r
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK);\r
        }\r
\r
        // Verify market is listed\r
        Market storage market = markets[address(cToken)];\r
        if (!market.isListed) {\r
            return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS);\r
        }\r
\r
        Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa});\r
\r
        // Check collateral factor <= 0.9\r
        Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa});\r
        if (lessThanExp(highLimit, newCollateralFactorExp)) {\r
            return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION);\r
        }\r
\r
        // If collateral factor != 0, fail if price == 0\r
        if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0) {\r
            return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE);\r
        }\r
\r
        // Set market's collateral factor to new collateral factor, remember old value\r
        uint oldCollateralFactorMantissa = market.collateralFactorMantissa;\r
        market.collateralFactorMantissa = newCollateralFactorMantissa;\r
\r
        // Emit event with asset, old collateral factor, and new collateral factor\r
        emit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
      * @notice Sets liquidationIncentive\r
      * @dev Admin function to set liquidationIncentive\r
      * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\r
      * @return uint 0=success, otherwise a failure. (See ErrorReporter for details)\r
      */\r
    function _setLiquidationIncentive(uint newLiquidationIncentiveMantissa) external returns (uint) {\r
        // Check caller is admin\r
        if (msg.sender != admin) {\r
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK);\r
        }\r
\r
        // Save current value for use in log\r
        uint oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\r
\r
        // Set liquidation incentive to new incentive\r
        liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\r
\r
        // Emit event with old incentive, new incentive\r
        emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    /**\r
      * @notice Add the market to the markets mapping and set it as listed\r
      * @dev Admin function to set isListed and add support for the market\r
      * @param cToken The address of the market (token) to list\r
      * @return uint 0=success, otherwise a failure. (See enum Error for details)\r
      */\r
    function _supportMarket(CToken cToken) external returns (uint) {\r
        if (msg.sender != admin) {\r
            return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK);\r
        }\r
\r
        if (markets[address(cToken)].isListed) {\r
            return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS);\r
        }\r
\r
        cToken.isCToken(); // Sanity check to make sure its really a CToken\r
\r
        // Note that isComped is not in active use anymore\r
        Market storage newMarket = markets[address(cToken)];\r
        newMarket.isListed = true;\r
        newMarket.isComped = false;\r
        newMarket.collateralFactorMantissa = 0;\r
\r
        _addMarketInternal(address(cToken));\r
        _initializeMarket(address(cToken));\r
\r
        emit MarketListed(cToken);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    function _addMarketInternal(address cToken) internal {\r
        for (uint i = 0; i < allMarkets.length; i ++) {\r
            require(allMarkets[i] != CToken(cToken), "market already added");\r
        }\r
        allMarkets.push(CToken(cToken));\r
    }\r
\r
    function _initializeMarket(address cToken) internal {\r
        uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits");\r
\r
        CompMarketState storage supplyState = compSupplyState[cToken];\r
        CompMarketState storage borrowState = compBorrowState[cToken];\r
\r
        /*\r
         * Update market state indices\r
         */\r
        if (supplyState.index == 0) {\r
            // Initialize supply state index with default value\r
            supplyState.index = compInitialIndex;\r
        }\r
\r
        if (borrowState.index == 0) {\r
            // Initialize borrow state index with default value\r
            borrowState.index = compInitialIndex;\r
        }\r
\r
        /*\r
         * Update market state block numbers\r
         */\r
         supplyState.block = borrowState.block = blockNumber;\r
    }\r
\r
\r
    /**\r
      * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\r
      * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing.\r
      * @param cTokens The addresses of the markets (tokens) to change the borrow caps for\r
      * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing.\r
      */\r
    function _setMarketBorrowCaps(CToken[] calldata cTokens, uint[] calldata newBorrowCaps) external {\r
    	require(msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps");\r
\r
        uint numMarkets = cTokens.length;\r
        uint numBorrowCaps = newBorrowCaps.length;\r
\r
        require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input");\r
\r
        for(uint i = 0; i < numMarkets; i++) {\r
            borrowCaps[address(cTokens[i])] = newBorrowCaps[i];\r
            emit NewBorrowCap(cTokens[i], newBorrowCaps[i]);\r
        }\r
    }\r
\r
    /**\r
     * @notice Admin function to change the Borrow Cap Guardian\r
     * @param newBorrowCapGuardian The address of the new Borrow Cap Guardian\r
     */\r
    function _setBorrowCapGuardian(address newBorrowCapGuardian) external {\r
        require(msg.sender == admin, "only admin can set borrow cap guardian");\r
\r
        // Save current value for inclusion in log\r
        address oldBorrowCapGuardian = borrowCapGuardian;\r
\r
        // Store borrowCapGuardian with value newBorrowCapGuardian\r
        borrowCapGuardian = newBorrowCapGuardian;\r
\r
        // Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian)\r
        emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian);\r
    }\r
\r
    /**\r
     * @notice Admin function to change the Pause Guardian\r
     * @param newPauseGuardian The address of the new Pause Guardian\r
     * @return uint 0=success, otherwise a failure. (See enum Error for details)\r
     */\r
    function _setPauseGuardian(address newPauseGuardian) public returns (uint) {\r
        if (msg.sender != admin) {\r
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK);\r
        }\r
\r
        // Save current value for inclusion in log\r
        address oldPauseGuardian = pauseGuardian;\r
\r
        // Store pauseGuardian with value newPauseGuardian\r
        pauseGuardian = newPauseGuardian;\r
\r
        // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian)\r
        emit NewPauseGuardian(oldPauseGuardian, pauseGuardian);\r
\r
        return uint(Error.NO_ERROR);\r
    }\r
\r
    function _setMintPaused(CToken cToken, bool state) public returns (bool) {\r
        require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");\r
        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");\r
        require(msg.sender == admin || state == true, "only admin can unpause");\r
\r
        mintGuardianPaused[address(cToken)] = state;\r
        emit ActionPaused(cToken, "Mint", state);\r
        return state;\r
    }\r
\r
    function _setBorrowPaused(CToken cToken, bool state) public returns (bool) {\r
        require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");\r
        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");\r
        require(msg.sender == admin || state == true, "only admin can unpause");\r
\r
        borrowGuardianPaused[address(cToken)] = state;\r
        emit ActionPaused(cToken, "Borrow", state);\r
        return state;\r
    }\r
\r
    function _setTransferPaused(bool state) public returns (bool) {\r
        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");\r
        require(msg.sender == admin || state == true, "only admin can unpause");\r
\r
        transferGuardianPaused = state;\r
        emit ActionPaused("Transfer", state);\r
        return state;\r
    }\r
\r
    function _setSeizePaused(bool state) public returns (bool) {\r
        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");\r
        require(msg.sender == admin || state == true, "only admin can unpause");\r
\r
        seizeGuardianPaused = state;\r
        emit ActionPaused("Seize", state);\r
        return state;\r
    }\r
\r
    function _become(Unitroller unitroller) public {\r
        require(msg.sender == unitroller.admin(), "only unitroller admin can change brains");\r
        require(unitroller._acceptImplementation() == 0, "change not authorized");\r
    }\r
\r
    /// @notice Delete this function after proposal 65 is executed\r
    function fixBadAccruals(address[] calldata affectedUsers, uint[] calldata amounts) external {\r
        require(msg.sender == admin, "Only admin can call this function"); // Only the timelock can call this function\r
        require(!proposal65FixExecuted, "Already executed this one-off function"); // Require that this function is only called once\r
        require(affectedUsers.length == amounts.length, "Invalid input");\r
\r
        // Loop variables\r
        address user;\r
        uint currentAccrual;\r
        uint amountToSubtract;\r
        uint newAccrual;\r
\r
        // Iterate through all affected users\r
        for (uint i = 0; i < affectedUsers.length; ++i) {\r
            user = affectedUsers[i];\r
            currentAccrual = compAccrued[user];\r
\r
            amountToSubtract = amounts[i];\r
\r
            // The case where the user has claimed and received an incorrect amount of COMP.\r
            // The user has less currently accrued than the amount they incorrectly received.\r
            if (amountToSubtract > currentAccrual) {\r
                // Amount of COMP the user owes the protocol\r
                uint accountReceivable = amountToSubtract - currentAccrual; // Underflow safe since amountToSubtract > currentAccrual\r
\r
                uint oldReceivable = compReceivable[user];\r
                uint newReceivable = add_(oldReceivable, accountReceivable);\r
\r
                // Accounting: record the COMP debt for the user\r
                compReceivable[user] = newReceivable;\r
\r
                emit CompReceivableUpdated(user, oldReceivable, newReceivable);\r
\r
                amountToSubtract = currentAccrual;\r
            }\r
\r
            if (amountToSubtract > 0) {\r
                // Subtract the bad accrual amount from what they have accrued.\r
                // Users will keep whatever they have correctly accrued.\r
                compAccrued[user] = newAccrual = sub_(currentAccrual, amountToSubtract);\r
\r
                emit CompAccruedAdjusted(user, currentAccrual, newAccrual);\r
            }\r
        }\r
\r
        proposal65FixExecuted = true; // Makes it so that this function cannot be called again\r
    }\r
\r
    /**\r
     * @notice Checks caller is admin, or this contract is becoming the new implementation\r
     */\r
    function adminOrInitializing() internal view returns (bool) {\r
        return msg.sender == admin || msg.sender == comptrollerImplementation;\r
    }\r
\r
    /*** Comp Distribution ***/\r
\r
    /**\r
     * @notice Set COMP speed for a single market\r
     * @param cToken The market whose COMP speed to update\r
     * @param supplySpeed New supply-side COMP speed for market\r
     * @param borrowSpeed New borrow-side COMP speed for market\r
     */\r
    function setCompSpeedInternal(CToken cToken, uint supplySpeed, uint borrowSpeed) internal {\r
        Market storage market = markets[address(cToken)];\r
        require(market.isListed, "comp market is not listed");\r
\r
        if (compSupplySpeeds[address(cToken)] != supplySpeed) {\r
            // Supply speed updated so let's update supply state to ensure that\r
            //  1. COMP accrued properly for the old speed, and\r
            //  2. COMP accrued at the new speed starts after this block.\r
            updateCompSupplyIndex(address(cToken));\r
\r
            // Update speed and emit event\r
            compSupplySpeeds[address(cToken)] = supplySpeed;\r
            emit CompSupplySpeedUpdated(cToken, supplySpeed);\r
        }\r
\r
        if (compBorrowSpeeds[address(cToken)] != borrowSpeed) {\r
            // Borrow speed updated so let's update borrow state to ensure that\r
            //  1. COMP accrued properly for the old speed, and\r
            //  2. COMP accrued at the new speed starts after this block.\r
            Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()});\r
            updateCompBorrowIndex(address(cToken), borrowIndex);\r
\r
            // Update speed and emit event\r
            compBorrowSpeeds[address(cToken)] = borrowSpeed;\r
            emit CompBorrowSpeedUpdated(cToken, borrowSpeed);\r
        }\r
    }\r
\r
    /**\r
     * @notice Accrue COMP to the market by updating the supply index\r
     * @param cToken The market whose supply index to update\r
     * @dev Index is a cumulative sum of the COMP per cToken accrued.\r
     */\r
    function updateCompSupplyIndex(address cToken) internal {\r
        CompMarketState storage supplyState = compSupplyState[cToken];\r
        uint supplySpeed = compSupplySpeeds[cToken];\r
        uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits");\r
        uint deltaBlocks = sub_(uint(blockNumber), uint(supplyState.block));\r
        if (deltaBlocks > 0 && supplySpeed > 0) {\r
            uint supplyTokens = CToken(cToken).totalSupply();\r
            uint compAccrued = mul_(deltaBlocks, supplySpeed);\r
            Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0});\r
            supplyState.index = safe224(add_(Double({mantissa: supplyState.index}), ratio).mantissa, "new index exceeds 224 bits");\r
            supplyState.block = blockNumber;\r
        } else if (deltaBlocks > 0) {\r
            supplyState.block = blockNumber;\r
        }\r
    }\r
\r
    /**\r
     * @notice Accrue COMP to the market by updating the borrow index\r
     * @param cToken The market whose borrow index to update\r
     * @dev Index is a cumulative sum of the COMP per cToken accrued.\r
     */\r
    function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal {\r
        CompMarketState storage borrowState = compBorrowState[cToken];\r
        uint borrowSpeed = compBorrowSpeeds[cToken];\r
        uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits");\r
        uint deltaBlocks = sub_(uint(blockNumber), uint(borrowState.block));\r
        if (deltaBlocks > 0 && borrowSpeed > 0) {\r
            uint borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex);\r
            uint compAccrued = mul_(deltaBlocks, borrowSpeed);\r
            Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0});\r
            borrowState.index = safe224(add_(Double({mantissa: borrowState.index}), ratio).mantissa, "new index exceeds 224 bits");\r
            borrowState.block = blockNumber;\r
        } else if (deltaBlocks > 0) {\r
            borrowState.block = blockNumber;\r
        }\r
    }\r
\r
    /**\r
     * @notice Calculate COMP accrued by a supplier and possibly transfer it to them\r
     * @param cToken The market in which the supplier is interacting\r
     * @param supplier The address of the supplier to distribute COMP to\r
     */\r
    function distributeSupplierComp(address cToken, address supplier) internal {\r
        // TODO: Don't distribute supplier COMP if the user is not in the supplier market.\r
        // This check should be as gas efficient as possible as distributeSupplierComp is called in many places.\r
        // - We really don't want to call an external contract as that's quite expensive.\r
\r
        CompMarketState storage supplyState = compSupplyState[cToken];\r
        uint supplyIndex = supplyState.index;\r
        uint supplierIndex = compSupplierIndex[cToken][supplier];\r
\r
        // Update supplier's index to the current index since we are distributing accrued COMP\r
        compSupplierIndex[cToken][supplier] = supplyIndex;\r
\r
        if (supplierIndex == 0 && supplyIndex >= compInitialIndex) {\r
            // Covers the case where users supplied tokens before the market's supply state index was set.\r
            // Rewards the user with COMP accrued from the start of when supplier rewards were first\r
            // set for the market.\r
            supplierIndex = compInitialIndex;\r
        }\r
\r
        // Calculate change in the cumulative sum of the COMP per cToken accrued\r
        Double memory deltaIndex = Double({mantissa: sub_(supplyIndex, supplierIndex)});\r
\r
        uint supplierTokens = CToken(cToken).balanceOf(supplier);\r
\r
        // Calculate COMP accrued: cTokenAmount * accruedPerCToken\r
        uint supplierDelta = mul_(supplierTokens, deltaIndex);\r
\r
        uint supplierAccrued = add_(compAccrued[supplier], supplierDelta);\r
        compAccrued[supplier] = supplierAccrued;\r
\r
        emit DistributedSupplierComp(CToken(cToken), supplier, supplierDelta, supplyIndex);\r
    }\r
\r
    /**\r
     * @notice Calculate COMP accrued by a borrower and possibly transfer it to them\r
     * @dev Borrowers will not begin to accrue until after the first interaction with the protocol.\r
     * @param cToken The market in which the borrower is interacting\r
     * @param borrower The address of the borrower to distribute COMP to\r
     */\r
    function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex) internal {\r
        // TODO: Don't distribute supplier COMP if the user is not in the borrower market.\r
        // This check should be as gas efficient as possible as distributeBorrowerComp is called in many places.\r
        // - We really don't want to call an external contract as that's quite expensive.\r
\r
        CompMarketState storage borrowState = compBorrowState[cToken];\r
        uint borrowIndex = borrowState.index;\r
        uint borrowerIndex = compBorrowerIndex[cToken][borrower];\r
\r
        // Update borrowers's index to the current index since we are distributing accrued COMP\r
        compBorrowerIndex[cToken][borrower] = borrowIndex;\r
\r
        if (borrowerIndex == 0 && borrowIndex >= compInitialIndex) {\r
            // Covers the case where users borrowed tokens before the market's borrow state index was set.\r
            // Rewards the user with COMP accrued from the start of when borrower rewards were first\r
            // set for the market.\r
            borrowerIndex = compInitialIndex;\r
        }\r
\r
        // Calculate change in the cumulative sum of the COMP per borrowed unit accrued\r
        Double memory deltaIndex = Double({mantissa: sub_(borrowIndex, borrowerIndex)});\r
\r
        uint borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex);\r
\r
        // Calculate COMP accrued: cTokenAmount * accruedPerBorrowedUnit\r
        uint borrowerDelta = mul_(borrowerAmount, deltaIndex);\r
\r
        uint borrowerAccrued = add_(compAccrued[borrower], borrowerDelta);\r
        compAccrued[borrower] = borrowerAccrued;\r
\r
        emit DistributedBorrowerComp(CToken(cToken), borrower, borrowerDelta, borrowIndex);\r
    }\r
\r
    /**\r
     * @notice Calculate additional accrued COMP for a contributor since last accrual\r
     * @param contributor The address to calculate contributor rewards for\r
     */\r
    function updateContributorRewards(address contributor) public {\r
        uint compSpeed = compContributorSpeeds[contributor];\r
        uint blockNumber = getBlockNumber();\r
        uint deltaBlocks = sub_(blockNumber, lastContributorBlock[contributor]);\r
        if (deltaBlocks > 0 && compSpeed > 0) {\r
            uint newAccrued = mul_(deltaBlocks, compSpeed);\r
            uint contributorAccrued = add_(compAccrued[contributor], newAccrued);\r
\r
            compAccrued[contributor] = contributorAccrued;\r
            lastContributorBlock[contributor] = blockNumber;\r
        }\r
    }\r
\r
    /**\r
     * @notice Claim all the comp accrued by holder in all markets\r
     * @param holder The address to claim COMP for\r
     */\r
    function claimComp(address holder) public {\r
        return claimComp(holder, allMarkets);\r
    }\r
\r
    /**\r
     * @notice Claim all the comp accrued by holder in the specified markets\r
     * @param holder The address to claim COMP for\r
     * @param cTokens The list of markets to claim COMP in\r
     */\r
    function claimComp(address holder, CToken[] memory cTokens) public {\r
        address[] memory holders = new address[](1);\r
        holders[0] = holder;\r
        claimComp(holders, cTokens, true, true);\r
    }\r
\r
    /**\r
     * @notice Claim all comp accrued by the holders\r
     * @param holders The addresses to claim COMP for\r
     * @param cTokens The list of markets to claim COMP in\r
     * @param borrowers Whether or not to claim COMP earned by borrowing\r
     * @param suppliers Whether or not to claim COMP earned by supplying\r
     */\r
    function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public {\r
        for (uint i = 0; i < cTokens.length; i++) {\r
            CToken cToken = cTokens[i];\r
            require(markets[address(cToken)].isListed, "market must be listed");\r
            if (borrowers == true) {\r
                Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()});\r
                updateCompBorrowIndex(address(cToken), borrowIndex);\r
                for (uint j = 0; j < holders.length; j++) {\r
                    distributeBorrowerComp(address(cToken), holders[j], borrowIndex);\r
                }\r
            }\r
            if (suppliers == true) {\r
                updateCompSupplyIndex(address(cToken));\r
                for (uint j = 0; j < holders.length; j++) {\r
                    distributeSupplierComp(address(cToken), holders[j]);\r
                }\r
            }\r
        }\r
        for (uint j = 0; j < holders.length; j++) {\r
            compAccrued[holders[j]] = grantCompInternal(holders[j], compAccrued[holders[j]]);\r
        }\r
    }\r
\r
    /**\r
     * @notice Transfer COMP to the user\r
     * @dev Note: If there is not enough COMP, we do not perform the transfer all.\r
     * @param user The address of the user to transfer COMP to\r
     * @param amount The amount of COMP to (possibly) transfer\r
     * @return The amount of COMP which was NOT transferred to the user\r
     */\r
    function grantCompInternal(address user, uint amount) internal returns (uint) {\r
        Comp comp = Comp(getCompAddress());\r
        uint compRemaining = comp.balanceOf(address(this));\r
        if (amount > 0 && amount <= compRemaining) {\r
            comp.transfer(user, amount);\r
            return 0;\r
        }\r
        return amount;\r
    }\r
\r
    /*** Comp Distribution Admin ***/\r
\r
    /**\r
     * @notice Transfer COMP to the recipient\r
     * @dev Note: If there is not enough COMP, we do not perform the transfer all.\r
     * @param recipient The address of the recipient to transfer COMP to\r
     * @param amount The amount of COMP to (possibly) transfer\r
     */\r
    function _grantComp(address recipient, uint amount) public {\r
        require(adminOrInitializing(), "only admin can grant comp");\r
        uint amountLeft = grantCompInternal(recipient, amount);\r
        require(amountLeft == 0, "insufficient comp for grant");\r
        emit CompGranted(recipient, amount);\r
    }\r
\r
    /**\r
     * @notice Set COMP borrow and supply speeds for the specified markets.\r
     * @param cTokens The markets whose COMP speed to update.\r
     * @param supplySpeeds New supply-side COMP speed for the corresponding market.\r
     * @param borrowSpeeds New borrow-side COMP speed for the corresponding market.\r
     */\r
    function _setCompSpeeds(CToken[] memory cTokens, uint[] memory supplySpeeds, uint[] memory borrowSpeeds) public {\r
        require(adminOrInitializing(), "only admin can set comp speed");\r
\r
        uint numTokens = cTokens.length;\r
        require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input");\r
\r
        for (uint i = 0; i < numTokens; ++i) {\r
            setCompSpeedInternal(cTokens[i], supplySpeeds[i], borrowSpeeds[i]);\r
        }\r
    }\r
\r
    /**\r
     * @notice Set COMP speed for a single contributor\r
     * @param contributor The contributor whose COMP speed to update\r
     * @param compSpeed New COMP speed for contributor\r
     */\r
    function _setContributorCompSpeed(address contributor, uint compSpeed) public {\r
    

Tags:
ERC20, Proxy, Mintable, Liquidity, Voting, Timelock, Upgradeable, Factory, Oracle|addr:0x069bd06d35ff7fae4165c764145de632261f086c|verified:true|block:23731464|tx:0xeb3dfa2eb37b76d7fc7aa6f407abec6dca828a47b16391c2f5502e87bc2dffff|first_check:1762345772

Submitted on: 2025-11-05 13:29:35

Comments

Log in to comment.

No comments yet.