sherlock-audit / 2024-09-predict-fun-judging

0 stars 0 forks source link

Glorious Tawny Jellyfish - Users accrue interest on the protocol fee #242

Open sherlock-admin4 opened 2 days ago

sherlock-admin4 commented 2 days ago

Glorious Tawny Jellyfish

High

Users accrue interest on the protocol fee

Summary

The Predict Fun protocol offers to its users the option to borrow some funds(USDB on blast, USDC on other chain) against a collateral in certain CTF tokens. When a loan is taken the protocol collects a fee. Borrowers are charged interest on the amount they borrow. However due to the way the protocol is implemented the fee that is collected by the protocol when a loan is made, is included in the amount that accrues interest. This will be detrimental for users that take out big loans. Consider the following example:

  1. Lets say Alice decides to accepts a loan offer from Bob via the acceptLoanOffer() function. The protocol charges a 2% fee which is equal to 200 BIPS. Bob has set the interestRatePerSecond to 3_020_262_040 which is supposed to be 10% according to the test files. The loanAmount is 100_000e18 the collateralAmount is 200_000e18. The loan duration is for 1 year.
  2. Now Alice is addicted to gambling and she is sure the bet she has taken is going to be a winning one, and would like to gamble more, so she decides to provide her 200_000e18 CTF tokens as collateral and take more USDB tokens, in order to buy more CTF tokens. So she calls the acceptLoanOffer() function with 100_000e18 as the value of the fulfillAmount parameter.
  3. In the _acceptOffer() function there are some checks done on the loan offer, and then the _calculateCollateralAmountRequired() function is called, which calculates the collateral that Alice needs to provide, this will be 200_000e18. Then the _acceptOffer() function calls the _transferLoanAmountAndProtocolFee() function:
    function _transferLoanAmountAndProtocolFee(
        address from,
        address to,
        uint256 loanAmount
    ) private returns (uint256 protocolFee) {
        protocolFee = (loanAmount * protocolFeeBasisPoints) / 10_000;
        LOAN_TOKEN.safeTransferFrom(from, to, loanAmount - protocolFee);
        if (protocolFee > 0) {
            LOAN_TOKEN.safeTransferFrom(from, protocolFeeRecipient, protocolFee);
        }
    }

    As can be seen from the above code snippet first the protocol fee is calculated (100_000e18 * 200) / 10_000 = 2_000e18, so Alice will receive only 98_000e18 the other 2_000e18 tokens will be sent to the fee receiver. However when we go back to he _acceptOffer() function we can see that the whole amount is used in the loan creation

    _createLoan(nextLoanId, proposal, positionId, lender, borrower, collateralAmountRequired, fulfillAmount);

    As a result Alice will incur interest rate on the full 100_000e18, not on the 98_000e18 tokens the she actually received. Lets say a year passes and she decides to repay her loan. She would have to repay 110_000e18 USDB in order to get back he collateral. Instead she should have repaid (98_000e18 * 10%) + 98_000e18 = 9_800e18 + 98_000e18 = 107.800e18

Keep in mind this numbers are chosen to better illustrate the problem. All loans are affected by the above described discrepancy. Another way to charge the fee should be implemented a way where the lender can lend the full amount, and a way where the borrower is not overcharged. This is not mentioned as a design decision, and if it is a decision decision is a terrible one, given that there are no docs or comments that make clear that the protocol desires to overcharge its borrowers, this should be considered a valid high. Nobody charges an interest on a fee that is supposed to be paid only once.

Root Cause

When a loan is created the fee that the protocol charges on loan creation is included in the total borrowed amount of the loan. Later on when debt is calculated the borrower accrues debt on the fee as well.

Internal pre-conditions

  1. The protocol is deployed and there is at least one borrower

External pre-conditions

No response

Attack Path

No response

Impact

Borrowers pay much more fees than they should, depending on the interest rate of the loan, the duration and the size of the loan borrowers may pay hundreds of dollars more than they should. This results in loss of fees for the borrowers in every loan, and this is the behavior of the protocol, there are no pre conditions or attacks, thus the high severity.

PoC

No response

Mitigation

As described in the above example Alice will receive only 98_000e18 but will have to pay interest on the 100_000e18, either give her the whole amount, and charge fee when she repays the loan, or figure out another way to collect fees, that doesn't overcharge borrowers.