Blend contract
CalculationHelpers.sol
// SPDX-License-Identifier: BSL 1.1 - Blend (c) Non Fungible Trading Ltd.
pragma solidity 0.8.17;
 
import "lib/solmate/src/utils/SignedWadMath.sol";
 
library CalculationHelpers {
    int256 private constant _YEAR_WAD = 365 days * 1e18;
    uint256 private constant _LIQUIDATION_THRESHOLD = 100_000;
    uint256 private constant _BASIS_POINTS = 10_000;
 
    /**
     * @dev Computes the current debt of a borrow given the last time it was touched and the last computed debt.
     * @param amount Principal in ETH
     * @param startTime Start time of the loan
     * @param rate Interest rate (in bips)
     * @dev Formula: https://www.desmos.com/calculator/l6omp0rwnh
     */
    function computeCurrentDebt(
        uint256 amount,
        uint256 rate,
        uint256 startTime
    ) external view returns (uint256) {
        uint256 loanTime = block.timestamp - startTime;
        int256 yearsWad = wadDiv(int256(loanTime) * 1e18, _YEAR_WAD);
        return uint256(wadMul(int256(amount), wadExp(wadMul(yearsWad, bipsToSignedWads(rate)))));
    }
 
    /**
     * @dev Calculates the current maximum interest rate a specific refinancing
     * auction could settle at currently given the auction's start block and duration.
     * @param startBlock The block the auction started at
     * @param oldRate Previous interest rate (in bips)
     * @dev Formula: https://www.desmos.com/calculator/urasr71dhb
     */
    function calcRefinancingAuctionRate(
        uint256 startBlock,
        uint256 auctionDuration,
        uint256 oldRate
    ) external view returns (uint256) {
        uint256 currentAuctionBlock = block.number - startBlock;
        int256 oldRateWads = bipsToSignedWads(oldRate);
 
        uint256 auctionT1 = auctionDuration / 5;
        uint256 auctionT2 = (4 * auctionDuration) / 5;
 
        int256 maxRateWads;
        {
            int256 aInverse = -bipsToSignedWads(15000);
            int256 b = 2;
            int256 maxMinRateWads = bipsToSignedWads(500);
 
            if (oldRateWads < -((b * aInverse) / 2)) {
                maxRateWads = maxMinRateWads + (oldRateWads ** 2) / aInverse + b * oldRateWads;
            } else {
                maxRateWads = maxMinRateWads - ((b ** 2) * aInverse) / 4;
            }
        }
 
        int256 startSlope = maxRateWads / int256(auctionT1); // wad-bips per block
 
        int256 middleSlope = bipsToSignedWads(9000) / int256(3 * auctionDuration / 5) + 1; // wad-bips per block (add one to account for rounding)
        int256 middleB = maxRateWads - int256(auctionT1) * middleSlope;
 
        if (currentAuctionBlock < auctionT1) {
            return signedWadsToBips(startSlope * int256(currentAuctionBlock));
        } else if (currentAuctionBlock < auctionT2) {
            return signedWadsToBips(middleSlope * int256(currentAuctionBlock) + middleB);
        } else if (currentAuctionBlock < auctionDuration) {
            int256 endSlope;
            int256 endB;
            {
                endSlope =
                    (bipsToSignedWads(_LIQUIDATION_THRESHOLD) -
                        ((int256(auctionT2) * middleSlope) + middleB)) /
                    int256(auctionDuration - auctionT2); // wad-bips per block
                endB =
                    bipsToSignedWads(_LIQUIDATION_THRESHOLD) -
                    int256(auctionDuration) *
                    endSlope;
            }
 
            return signedWadsToBips(endSlope * int256(currentAuctionBlock) + endB);
        } else {
            return _LIQUIDATION_THRESHOLD;
        }
    }
 
    /**
     * @dev Converts an integer bips value to a signed wad value.
     */
    function bipsToSignedWads(uint256 bips) public pure returns (int256) {
        return int256((bips * 1e18) / _BASIS_POINTS);
    }
 
    /**
     * @dev Converts a signed wad value to an integer bips value.
     */
    function signedWadsToBips(int256 wads) public pure returns (uint256) {
        return uint256((wads * int256(_BASIS_POINTS)) / 1e18);
    }
}