Light ModeLight
Light ModeDark

One Bug Per Day

One H/M every day from top Wardens

Checkmark

Join over 1115 wardens!

Checkmark

Receive the email at any hour!

Ad

LendingTerm Inconsistency between debt ceiling as calculated in borrow() and debtCeiling()

mediumCode4rena

Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/LendingTerm.sol#L394-L397

Vulnerability details

Impact

There is an inconsistency in the calculation of the debtCeiling between the borrow() function and the debtCeiling() function. This not only leads to operational discrepancies but also impacts liquidity utilization. Specifically, the more restrictive debtCeiling in the borrow() function results in underutilized liquidity, which in turn could lead to missed profit opportunities for lenders.

Proof of Concept

There is an inconsistency in the way borrow() calculates a much smaller value for debtCeiling than the actual debtCeiling() function. This renders this check useless since borrow prevents issuance from going even close to the actual debt ceiling.

solidity
uint256 debtCeilingAfterDecrement = LendingTerm(gauge).debtCeiling(-int256(weight)); require(issuance <= debtCeilingAfterDecrement, "GuildToken: debt ceiling used");

Let's take the parameters below to illustrate the issue and follow along the two calculations:

| Parameter | Value | |------------------------|------------------------------------------------| | Issuance | 20,000 | | Total Borrowed Credit | 70,000 (5k when we call borrow + 65k old loans)| | Total Weight | 100,000 | | Gauge Weight | 50,000 | | Gauge Weight Tolerance | 60% (1.2e18) |

1. borrow() function's calculation method:

  • This function calculates the debtCeiling using a simpler formula:

$$ \begin{align*} \text{debtCeiling} &= \frac{{(\text{Gauge Weight} \times (\text{Total Borrowed Credit} + \text{Borrow Amount}))}}{{\text{Total Weight}}} \times \text{Gauge Weight Tolerance} \end{align*} $$

  • Applying the provided parameters (Gauge Weight: 50,000, Total Borrowed Credit: 70,000, Total Weight: 100,000, and Weight Tolerance: 1.2), the resulting debtCeiling is 42,000.

2. debtCeiling() function's calculation method:

  • The formula used here is more complex so we will it break it down below:

$$ \text{debtCeiling} = \left( \left( \left( \frac{\text{Total Borrowed Credit} \times (\text{Gauge Weight} \times 1.2e18)}{\text{Total Weight}} \right) - \text{Issuance} \right) \times \frac{\text{Total Weight}}{\text{Other Gauges Weight}} \right) + \text{Issuance} $$

  • This method involves several intermediate steps to arrive at the debtCeiling. The process includes:
    • Calculating the tolerated gauge weight.
    • Determining the initial debt ceiling before any new borrowings.
    • Computing the remaining debt ceiling after factoring in current issuance.
    • Establishing the weight of other gauges.
    • Determining the maximum borrowable amount.
    • Adding the current issuance to the maximum borrow to get the final debtCeiling.

toleratedGaugeWeight

$$ \begin{align*} \text{toleratedGaugeWeight} &= \frac{{\text{gaugeWeight} \times \text{gaugeWeightTolerance}}}{{1e18}} \ &= \frac{{50000e18 \times 1.2e18}}{{1e18}} \ &= 60000e18 \end{align*} $$

debtCeilingBefore

$$ \begin{align*} \text{debtCeilingBefore} &= \frac{{\text{totalBorrowedCredit} \times \text{toleratedGaugeWeight}}}{{\text{totalWeight}}} \ &= \frac{{70000e18 \times 60000e18}}{{100000e18}} \ &= 42000e18 \end{align*} $$

remainingDebtCeiling

$$ \begin{align*} \text{remainingDebtCeiling} &= \text{debtCeilingBefore} - \text{issuance} \ &= 42000e18 - 20000e18 \ &= 22000e18 \end{align*} $$

otherGaugesWeight

$$ \begin{align*} \text{otherGaugesWeight} &= \text{totalWeight} - \text{toleratedGaugeWeight} \ &= 100000e18 - 60000e18 \ &= 40000e18 \end{align*} $$

maxBorrow

$$ \begin{align*} \text{maxBorrow} &= \frac{{\text{remainingDebtCeiling} \times \text{totalWeight}}}{{\text{otherGaugesWeight}}} \ &= \frac{{22000e18 \times 100000e18}}{{40000e18}} \ &= 55000e18 \end{align*} $$

debtCeiling

$$ \begin{align*} \text{debtCeiling} &= \text{issuance} + \text{maxBorrow} \ &= 55000e18 + 20000e18 \ &= 75000e18 \end{align*} $$

With the same parameters, this method results in a debtCeiling of 75,000.

The lower debtCeiling set by the borrow() function (42,000) significantly restricts the amount that can be borrowed compared to what is actually permissible as per the debtCeiling() function (75,000).

This discrepancy leads to a situation where a portion of the liquidity remains unused. In a lending scenario, unused liquidity equates to lost income opportunities for lenders, as these funds are not being loaned out and thus not generating interest.

Tools Used

Manual review.

Recommended Mitigation Steps

Unify the debtCeiling calculation method which is used across the protocol.

Assessed type

Error