Lucene search

K
code423n4Code4renaCODE423N4:2023-11-BETAFINANCE-FINDINGS-ISSUES-8
HistoryNov 02, 2023 - 12:00 a.m.

accure interest function is likely failed to accure interest for token with low decimal

2023-11-0200:00:00
Code4rena
github.com
3
vulnerability
accruing interest
token decimals
precision loss
proof of concept
hardcoded decimal
test case
rounding down
interest accrual
security issue

7 High

AI Score

Confidence

Low

Lines of code

Vulnerability details

Impact

loss of precision is too high when accuring interest

Proof of Concept

When intereste accures, we are calling

uint256 interestAmount;
{
	uint256 interestRate = IIRM(irm).getInterestRate(address(this), trancheIndex, totalDeposit, totalBorrow);
	interestAmount = (trancheBorrowAmount_ * interestRate * timePassed) / 365 days / IRM_SCALE;
}

note that the loss of precision is too high:

	interestAmount = (trancheBorrowAmount_ * interestRate * timePassed) / 365 days / IRM_SCALE;

we are dividing 365 days and then divde by IRM_SCALE (which is 1e9)

combing the fact taht the IRM_SCALE is hardcoded to 1e9

and if the time passed between two accuring is small,

and the underlying token have low decimals (6 decimals, even two decimals)

the interest accured will be always rounded to 0

In tests/MockERC20.sol,

the decimal is hardcoded to 18

we change the hardcode decimal to 6

and we add the POC below in TestOmniToken.t.sol

    function test_Accrue_POC_Low_decimal() public {
        setUpBorrow();
        (uint256 tda0, uint256 tba0,,) = oToken.tranches(0);
        (uint256 tda1, uint256 tba1,,) = oToken.tranches(1);
        (uint256 tda2, uint256 tba2,,) = oToken.tranches(2);
        uint256 td = tda0 + tda1 + tda2;

        uint256 borrowAmount = 10 * (10 ** uToken.decimals());
        IOmniPool(pool).borrow(0, address(oToken), borrowAmount);

        uint256 time_elapse = 120 seconds;
        vm.warp(time_elapse);

        uint256 interestAmount;
        {
            uint256 interestRate = irm.getInterestRate(address(oToken), 0, td, borrowAmount);
            interestAmount = borrowAmount * interestRate * time_elapse / 365 days / oToken.IRM_SCALE();
        }
        uint256 feeInterestAmount = interestAmount * oToken.RESERVE_FEE() / oToken.FEE_SCALE();
        interestAmount -= feeInterestAmount;

        oToken.accrue();

        vm.warp(time_elapse * 2);

        oToken.accrue();

        vm.warp(time_elapse * 4);

        oToken.accrue();
    
    }

we are accuring the interest every two minutes

before running the POC

we import the

import "forge-std/console.sol";

In OmniToken.sol

and add the console.log to log the interest

{
	uint256 interestRate = IIRM(irm).getInterestRate(address(this), trancheIndex, totalDeposit, totalBorrow);
	// @audit
	// this can be trancated to 0
	interestAmount = (trancheBorrowAmount_ * interestRate * timePassed) / 365 days / IRM_SCALE;
	console.log("interestAmount", interestAmount);
}

then we run the POC

forge test -vv --match-test "test_Accrue_POC_Low_decimal"

the output is

Running 1 test for src/tests/TestOmniToken.t.sol:TestOmniToken
[PASS] test_Accrue_POC_Low_decimal() (gas: 737833)
Logs:
  interestAmount 0
  interestAmount 0
  interestAmount 0

this mean within every two minutes time elapse, the interest is round down to 0

and the accure function is called in every borrow / withdraw / transfer, so the interest will be rounded down heaviliy

or user can keep calling accure for every two minutes (or x minutes) to avoid paying interest

Tools Used

Manual Review

Recommended Mitigation Steps

modify the interest, do not do muliplication before division to avoid precision loss

do not hardcode IRM_SCALE and consider token decimals when accuring interess

Assessed type

Decimal


The text was updated successfully, but these errors were encountered:

All reactions

7 High

AI Score

Confidence

Low