Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loans with a minimum borrowing amount can not be partially repaid #1182

Closed
c4-bot-6 opened this issue Dec 28, 2023 · 12 comments
Closed

Loans with a minimum borrowing amount can not be partially repaid #1182

c4-bot-6 opened this issue Dec 28, 2023 · 12 comments
Labels
bug Something isn't working disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) downgraded by judge Judge downgraded the risk level of this issue grade-c high quality report This report is of especially high quality primary issue Highest quality submission among a set of duplicates QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@c4-bot-6
Copy link
Contributor

Lines of code

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

Vulnerability details

Impact

Loans with a minimum borrowing amount can not be partially repaid.

The main reason a borrower wants to partially repay his loan is to prevent his position from going underwater due to accruing interest. The lending terms require a minimum percentage to cover the debt during partial repayment. Also, when a borrower creates a new loan, there is a restriction on the minimum credit amount that can be borrowed. The restriction for the minimum borrow amount is to ensure that the gas costs of liquidation do not outsize the minimum overcollateralization.

// check that enough CREDIT is borrowed
require(
    borrowAmount >= ProfitManager(refs.profitManager).minBorrow(),
    "LendingTerm: borrow amount too low"
);

When a borrower creates a loan with borrowAmount == ProfitManager(refs.profitManager).minBorrow(), it is not possible for him to partially repay his debt. The problem arises from this require statement:

 require(
    borrowAmount - issuanceDecrease >
        ProfitManager(refs.profitManager).minBorrow(),
    "LendingTerm: below min borrow"
);

In this scenario borrowAmount == ProfitManager(refs.profitManager).minBorrow() and the require statement will never be true. Additionally, loans with a borrow amount close to minBorrow can not be partially repaid because uint256 issuanceDecrease = (borrowAmount * percentRepaid) / 1e18;, where percentRepaid = (debtToRepay * 1e18) / loanDebt. In the best-case scenario for the borrower, he will repay the minimum partial repayment percentage from the borrowed amount.

Every newly created loan with a borrowAmount close to the minBorrowAmount can not be partially repaid. Lenders can not prevent their position from going underwater because they can not partially repay their loans as expected at the beginning of the loan creation.

Proof of Concept

Paste the test in LendingTerm.t.sol

 function testLoansWithMinimumBorrowAmountCanNotBePartialRepaid() public {
        /* 
        LendingTerm params:
            maxDebtPerCollateralToken = 2000e18;     -> 2000, same decimals
            interestRate = 0.10e18;                  -> 10% APR
            maxDelayBetweenPartialRepay = 63115200;  -> 2 years
            minPartialRepayPercent = 0.2e18;         -> 20%
            openingFee = 0;                          -> 0%
        */

        // prepare & borrow
        uint256 minBorrowAmount = profitManager.minBorrow(); // borrowAmount
        uint256 collateralAmount = 5e16;

        collateral.mint(address(this), collateralAmount);
        collateral.approve(address(term), collateralAmount);
        bytes32 loanId = term.borrow(minBorrowAmount, collateralAmount);
        assertEq(term.getLoan(loanId).collateralAmount, collateralAmount);

        // partialRepay
        vm.warp(block.timestamp + term.YEAR());
        vm.roll(block.number + 1);

        // The debt is 110e18 after 1 year.
        assertEq(term.getLoanDebt(loanId), 110e18);
       
        // User pay minimum partial repay percent which is 20%.
        credit.mint(address(this), 22e18);
        credit.approve(address(term), 22e18);

        // The transaction revert because
        // (borrowAmount - issuanceDecrease) is lower than minBorrow.
        vm.expectRevert("LendingTerm: below min borrow");
        term.partialRepay(loanId, 24e18);

        //MIN_BORROW increases when creditMultiplier decreases - partialRepay()
    }

Tools Used

Mannual Review

Recommended Mitigation Steps

Pre-calculate the minimum borrow amount. It should be bigger than ProfitManager(refs.profit Manager).minBorrow().

Assessed type

Invalid Validation

@c4-bot-6 c4-bot-6 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Dec 28, 2023
c4-bot-3 added a commit that referenced this issue Dec 28, 2023
@0xSorryNotSorry
Copy link

The issue is well demonstrated, properly formatted, contains a coded POC.
Marking as HQ.

@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as high quality report

@c4-pre-sort c4-pre-sort added the high quality report This report is of especially high quality label Jan 1, 2024
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as primary issue

@eswak
Copy link

eswak commented Jan 18, 2024

Acknowledging this, this is fine IMO because at worst, the loan will miss a partial payment and will be called, it can't be decreased below minBorrow size and should be safe to send into liquidation. We could add a warning on the front-end but this seems like a pretty rare edge case. Disagree with severity because no user funds are at risk and it doesn't prevent the protocol from functioning properly, should be informational imo.

@c4-sponsor c4-sponsor added the sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons label Jan 18, 2024
@c4-sponsor
Copy link

eswak (sponsor) acknowledged

@c4-sponsor
Copy link

eswak marked the issue as disagree with severity

@c4-sponsor c4-sponsor added the disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) label Jan 18, 2024
@Trumpero
Copy link

Agree with sponsor that this issue should be a QA/info

@c4-judge
Copy link
Contributor

Trumpero changed the severity to QA (Quality Assurance)

@c4-judge c4-judge added downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 3 (High Risk) Assets can be stolen/lost/compromised directly labels Jan 28, 2024
@c4-judge
Copy link
Contributor

Trumpero marked the issue as grade-b

@c4-judge c4-judge added grade-c unsatisfactory does not satisfy C4 submission criteria; not eligible for awards and removed grade-b labels Jan 31, 2024
@c4-judge
Copy link
Contributor

Trumpero marked the issue as grade-c

@kazantseff
Copy link

Hey @Trumpero, I want to understand the difference between this issue and #756 . In both cases the functionality of the protocol is impacted as borrower won't be able to partialy repay his loan, so the impact is the same while the root cause is different. So the question is why this deemed unsatisfactory while #756 is of medium severity? Thanks you!

@Trumpero
Copy link

Trumpero commented Feb 3, 2024

@kazantseff
This issue and #756 have different root causes and impacts. In this issue, users can't partially repay if the remaining borrow amount is too small (below minBorrow). It's not a problem because when the borrow amount is too small, it's an intended design to not allow dividing it smaller. Therefore, users should be aware of it and repay fully for small loans. However, issue #756 represents the inability of partial repayment even in normal conditions and huge loans. Partial repayment will be inactive or bricked when a lending term has a small interest rate (near or equal to 0%), or users repay too quickly. So, #756 has a medium severity impact, while this issue should be a QA/info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) downgraded by judge Judge downgraded the risk level of this issue grade-c high quality report This report is of especially high quality primary issue Highest quality submission among a set of duplicates QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

8 participants