// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import {FixedPointMathLib} from "./Marketplace/FixedPointMathLib.sol"; 
import "./ExponentialNoError.sol";
import "./Marketplace/Interfaces.sol";

/// @notice Get up to date cToken data without mutating state.  
/// @author Transmissions11 (https://github.com/transmissions11/libcompound)
library LibCompound {

    error RATE();
    
    struct Exp {
        uint mantissa;
    }

    struct Double {
        uint mantissa;
    }
    uint constant expScale = 1e18;
    uint constant doubleScale = 1e36;
    uint constant halfExpScale = expScale/2;
    uint constant mantissaOne = expScale;

    /**
     * @dev Truncates the given exp to a whole number value.
     *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
     */
    function truncate(Exp memory exp) pure internal returns (uint) {
        // Note: We are not using careful math here as we're performing a division that cannot fail
        return exp.mantissa / expScale;
    }

    /**
     * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
     */
    function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) {
        Exp memory product = mul_(a, scalar);
        return truncate(product);
    }

    /**
     * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
     */
    function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) {
        Exp memory product = mul_(a, scalar);
        return (truncate(product) + addend);
    }

    function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
        return Exp({mantissa: a.mantissa * b});
    }

    using FixedPointMathLib for uint256;

    function viewUnderlyingBalanceOf(ICERC20 cToken, address user) internal view returns (uint256) {
        return cToken.balanceOf(user).mulWadDown(viewExchangeRate(cToken));
    }

    function viewExchangeRate(ICERC20 cToken) internal view returns (uint256) {
        uint256 accrualBlockNumberPrior = cToken.accrualBlockNumber();
        uint256 currentBlockNumber = block.number;

        if (accrualBlockNumberPrior == currentBlockNumber) return cToken.exchangeRateStored();

        uint256 totalCash = cToken.underlying().balanceOf(address(cToken));
        uint256 borrowsPrior = cToken.totalBorrows();
        uint256 reservesPrior = cToken.totalReserves();

        uint256 borrowRateMantissa = cToken.interestRateModel().getBorrowRate(totalCash, borrowsPrior, reservesPrior);

        if(borrowRateMantissa > 0.0005e16) { revert RATE(); } // Same as borrowRateMaxMantissa in CTokenInterfaces.sol

        /* Calculate the number of blocks elapsed since the last accrual */
        uint blockDelta = currentBlockNumber - accrualBlockNumberPrior;

        Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), blockDelta);
        uint interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);
        uint totalBorrowsNew = interestAccumulated + borrowsPrior;
        uint totalReservesNew = mul_ScalarTruncateAddUInt(Exp({mantissa: cToken.reserveFactorMantissa()}), interestAccumulated, reservesPrior);

        uint cashPlusBorrowsMinusReserves = totalCash + totalBorrowsNew - totalReservesNew;
        uint exchangeRate = cashPlusBorrowsMinusReserves * expScale / cToken.totalSupply();

        return (exchangeRate);
    }
}

By admin

Leave a Reply

Your email address will not be published.