<!-- canonical: https://0xsimao.com/findings/autonomint-dos-liquidation-underflow-computation -->

# DOS on liquidation type 1 due to underflow in cds profits computation

Medium · Sherlock · Hedged stablecoin · 4th December 2024

Finding M-27 of the Autonomint competition.

- Protocol: https://audits.sherlock.xyz/contests/569
- Report: /reports/autonomint
- Codebase: https://github.com/0xsimao/2024-11-autonomint/tree/0d324e04d4c0ca306e1ae4d4c65f0cb9d681751b
- Source: https://github.com/sherlock-audit/2024-11-autonomint-judging/issues/840

---

### Summary

An underflow in [cds profits computation](https://github.com/sherlock-audit/2024-11-autonomint/blob/main/Blockchain/Blockchian/contracts/Core_logic/borrowLiquidation.sol#L211-L212) can cause liquidation type 1 to revert.

### Root Cause

Here is the formula for cds profits computation. It is possible that **returnToTreasury** (aka as borrowerDebt) can be greater than **deposited amount value** (((depositDetail.depositedAmountInETH * depositDetail.ethPriceAtDeposit) / BorrowLib.USDA_PRECISION) / 100)  which can cause the formula to revert and DOS the liquidation type 1 process.

```Solidity
File: borrowLiquidation.sol
211:         // Calculate the CDS profits, USDA precision is 1e12, @audit need to double check if profit formula is right logic or can underflow
212:         uint128 cdsProfits = (((depositDetail.depositedAmountInETH * depositDetail.ethPriceAtDeposit) / BorrowLib.USDA_PRECISION) / 100) - returnToTreasury - returnToAbond;
```

Here is the details of returntoTreasury which is equivalent to borrower's debt amount during time of liquidation. This includes interest accumulated from loan creation up to liquidation time. If lastCumulativeRate reach a certain percentage like 126% in my example issue, borrowerDebt will become greater than **deposited amount value**  ((depositDetail.depositedAmountInETH * depositDetail.ethPriceAtDeposit) / BorrowLib.USDA_PRECISION) / 100) which can cause underflow error.

```Solidity
File: borrowLiquidation.sol
204:         // Calculate borrower's debt, rate precision is 1e27, this includes interest accumulated from creation of loan to liquidation
205:         uint256 borrowerDebt = ((depositDetail.normalizedAmount * lastCumulativeRate) / BorrowLib.RATE_PRECISION);
206:         uint128 returnToTreasury = uint128(borrowerDebt);
```

Here is the details of returnToAbond which represents 10% of deposited amount after subtracting returntotreasury.
```Solidity
File: borrowLiquidation.sol
209:         uint128 returnToAbond = BorrowLib.calculateReturnToAbond(depositDetail.depositedAmountInETH, depositDetail.ethPriceAtDeposit, returnToTreasury);
```
```Solidity
File: BorrowLib.sol
137:     function calculateReturnToAbond(
138:         uint128 depositedAmount,
139:         uint128 depositEthPrice,
140:         uint128 returnToTreasury
141:     ) public pure returns (uint128) {
142:         // 10% of the remaining amount, USDA precision is 1e12
143:         return (((((depositedAmount * depositEthPrice) / USDA_PRECISION) / 100) - returnToTreasury) * 10) / 100;
144:     }
```
Regarding the lastcumulativerate on how it exactly increases, it can be computed by the following below. In line 245, there is [_rpow](https://github.com/sherlock-audit/2024-11-autonomint/blob/main/Blockchain/Blockchian/contracts/lib/BorrowLib.sol#L1038-L1056) computation in which time interval, interest rate per second are factors in increasing the last cumulative rate. The more time the loan is not liquidated or not paid, the more the loan get bigger due to interest rate which will eventually reach 126%. The cumulative rate variable should start or set at around 100% based on set APR after protocol deployed the borrowing contract, so the very first loan will use the cumulative rate at this rate, then will increase from there moving forward.

```Solidity
File: BorrowLib.sol
222:     /**
223:      * @dev calculates cumulative rate
224:      * @param noOfBorrowers total number of borrowers in the protocol
225:      * @param ratePerSec interest rate per second
226:      * @param lastEventTime last event timestamp
227:      * @param lastCumulativeRate previous cumulative rate
228:      */
229:     function calculateCumulativeRate(
230:         uint128 noOfBorrowers,
231:         uint256 ratePerSec,
232:         uint128 lastEventTime,
233:         uint256 lastCumulativeRate
234:     ) public view returns (uint256) {
235:         uint256 currentCumulativeRate;
236: 
237:         // If there is no borrowers in the protocol
238:         if (noOfBorrowers == 0) {
239:             // current cumulative rate is same as ratePeSec
240:             currentCumulativeRate = ratePerSec;
241:         } else {
242:             // Find time interval between last event and now
243:             uint256 timeInterval = uint128(block.timestamp) - lastEventTime;
244:             //calculate cumulative rate
245:             currentCumulativeRate = lastCumulativeRate * _rpow(ratePerSec, timeInterval, RATE_PRECISION);
246:             currentCumulativeRate = currentCumulativeRate / RATE_PRECISION;
247:         }
248:         return currentCumulativeRate;
249:     }
```


Let's further discuss the exact situation on the attack path to describe the issue on more details.


### Internal pre-conditions

If the cumulative rate exceed certain percentage like 125% in my sample issue, it will cause revert to the cds profits computation.

### External pre-conditions

_No response_

### Attack Path

Let's consider this scenario wherein the 

**Borrower deposit details at time of loan creation:**
Deposited amount in eth  = 50 eth
Ether price at deposit time = 3000 usd price of 1 Eth per oracle during deposit

**During time of liquidation**
lastCumulativerate is 126% or 1.26e27

1. Let's compute first the returnToTreasury or borrower's debt during time of liquidation. In this case, we use 126% to represent the borrower's debt value in percentage. It increased to that figure due to interest accumulated since loan creation up to liquidation. The amount of returnToTreasury here is 1.512e11 or 151,200 usd
<img width="915" alt="image" src="https://sherlock-files.ams3.digitaloceanspaces.com/gh-images/9b515d43-ea6b-41b0-9a86-d5bdf01005db" />



2. Let's compute the returnToabond as part also of formula, in this case, we already encountered a problem of underflow error due to subtraction of larger returnToTreasury. See below 
<img width="841" alt="image" src="https://sherlock-files.ams3.digitaloceanspaces.com/gh-images/9a02f62c-0dc5-481b-a34b-182f8a5e33ec" />

3. As you can see in this CDS profit computation below, there is already underflow error when the bigger returnToTreasury is subtracted to deposited amount value.
<img width="1153" alt="image" src="https://sherlock-files.ams3.digitaloceanspaces.com/gh-images/c1c8fafd-a1d8-4fc8-bb64-5da4de8a27f5" />


Here is the link of google sheet for [computation copy](https://docs.google.com/spreadsheets/d/1Cmek971NLPn-PoYOFyPdtvBWqUEE11kHzgdkAg_vXts/edit?usp=drive_link)


### Impact

Interruption of liquidation type 1 process. The loan is not liquidatable.

### PoC

See attack path

### Mitigation

Revise the computation of cds profits, it should not revert in any way possible so the liquidation won't be interrupted.

---

Related findings:

- [Index underflow is not protected against, although it has no impact as the pool with index type(uint256).max should not be registered](https://0xsimao.com/findings/nftperp-ii-index-underflow-impact-registered): Nftperp Matching Engine
