xanoutas/solidity-security-findings huggingface dataset:
Concurrent disputes cause inconsistent state updates, compromising redemption mechanism reliability.
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L224-L301


# Vulnerability details

_When multiple users attempt to dispute the same redemption proposal simultaneously, race condition will occur in the following scenario:_

a. User A and User B both identify an incorrect redemption proposal and decide to dispute it.
b. User A and User B both retrieve the redeemer's asset user record and the disputing ShortRecord.
c. User A and User B both check the validity of their disputes and find them to be valid.
d. User A and User B both proceed to remove the incorrect proposals and update the state variables.

> The fact that the `disputeRedemption` function does not handle concurrent disputes properly. If User A and User B's transactions are executed in quick succession or included in the same block, the state updates performed by one user may be overwritten by the other, leading to inconsistent or incorrect dispute resolutions.

```solidity
// Remove the incorrect proposals and update state variables
259:  if (disputeCR < incorrectProposal.CR && disputeSR.updatedAt + C.DISPUTE_REDEMPTION_BUFFER <= redeemerAssetUser.timeProposed) {
// ...
```

This performs the state updates without considering the possibility of concurrent disputes.

> `disputeRedemption` function is to allow users to dispute redemption proposals by providing a ShortRecord with a lower collateral ratio (CR) than the one being disputed. 

- `asset`: The address of the asset being disputed.
- `redeemer`: The address of the user who proposed the redemption.
- `incorrectIndex`: The index of the incorrect proposal in the redeemer's proposal list.
- `disputeShorter`: The address of the shorter associated with the disputing ShortRecord.
- `disputeShortId`: The ID of the disputing ShortRecord.

[RedemptionFacet.sol#disputeRedemption](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L224-L301)

```solidity
function disputeRedemption(
    address asset,
    address redeemer,
    uint8 incorrectIndex,
    address disputeShorter,
    uint8 disputeShortId
) external {
    // ...

    // Check if the dispute is valid
    if (disputeCR < incorrectProposal.CR && disputeSR.updatedAt + C.DISPUTE_REDEMPTION_BUFFER <= redeemerAssetUser.timeProposed) {      <----------@
        // Remove the incorrect proposals and update state variables
        // ...
    } else {
        revert Errors.InvalidRedemptionDispute();
    }
}
```
> The function first retrieves the necessary data, such as the redeemer's asset user record (`redeemerAssetUser`) and the disputing ShortRecord (`disputeSR`). It then checks if the dispute is valid by comparing the CR of the disputing ShortRecord with the CR of the incorrect proposal. If the dispute is valid, the function removes the incorrect proposals from the redeemer's proposal list and updates the releva
legitimate users may lose their redemption opportunities, and the redemption process becomes unfair and biased in favor of attackers.
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L56-L61


# Vulnerability details

`proposeRedemption`, allows users to submit a proposal for redeeming their dUSD for collateral from ShortRecords. The function takes the following inputs:
- `asset`: The address of the asset being redeemed.
- `proposalInput`: An array of `MTypes.ProposalInput` structs, each containing a ShortRecord's `shorter` address, `shortId`, and `shortOrderId`.
- `redemptionAmount`: The total amount of dUSD the user wants to redeem.
- `maxRedemptionFee`: The maximum fee the user is willing to pay for the redemption.

[RedemptionFacet.sol#proposeRedemption](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L56-L61)

> The `proposeRedemption` function is expected to allow users to propose redemptions for ShortRecords with a CR below the maximum redemption CR. The proposals should be processed in a fair and unbiased manner, based on factors like the CR, redemption amount, and time of submission.

```solidity
function proposeRedemption(
    address asset,
    MTypes.ProposalInput[] calldata proposalInput,
    uint88 redemptionAmount,
    uint88 maxRedemptionFee
) external isNotFrozen(asset) nonReentrant {
    // Function body
}
```
Lack of front-running protection in the `proposeRedemption` function. An attacker can monitor the mempool for incoming redemption proposals and quickly submit their own proposal with a lower CR, effectively front-running the legitimate proposal.

The issue lies in the fact that the function processes the redemption proposals in the order they are received, without any considerations for the CR or the time of submission. This allows an attacker to manipulate the redemption order and potentially steal the redemption opportunity from legitimate users.

## Impact
The redemption process becomes unfair and biased, favoring attackers over legitimate users.

## Proof of Concept
Let's consider a scenario where multiple users want to propose redemptions for ShortRecords with different CRs. The ShortRecords and their corresponding CRs are as follows:

- ShortRecord 1: CR = 1.5
- ShortRecord 2: CR = 1.6
- ShortRecord 3: CR = 1.7

**Legitimate User's Actions:**
1. A legitimate user, Alice, wants to propose a redemption for ShortRecord 1, which has a CR of 1.5.
2. Alice calls the `proposeRedemption` function with the following parameters:
   - `asset`: The address of the asset being redeemed.
   - `proposalInput`: An array containing the `shorter` address, `shortId`, and `shortOrderId` of ShortRecord 1.
   - `redemptionAmount`: The amount of dUSD Alice wants to redeem.
   - `maxRedemptionFee`: The maximum fee Alice is willing to pay.
3. Alice's transaction is broadcasted to the Ethereum network and enters the mempool.

**Attacker's Actions:**
1. An attacker, Eve, is monitoring the mempool fo
proposeRedemption() uses old oracle price
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L75


# Vulnerability details

## Impact
[`proposeRedemption()`](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L75) does not update asset price when called. If the protocol functions that update the price are not called for some time the real asset price can change but during the proposal of redemption, the protocol will calculate the CR according to the old saved price.

In such conditions, users are able to make redemptions to Short Records with healthy CRs (that are above redemptionCR)

## Proof of Concept
Run the test with `forge test --mt test_oldOraclePrice -vv`.
Paste the following on the bottom of the BidOrders.t.sol file for example:

```solidity
function proposeRedemption(address account, uint88 redemptionAmount, address shorter, uint8 shortId) private {
        depositEth(account, MAX_REDEMPTION_FEE);
        MTypes.ProposalInput[] memory proposalInputs = new MTypes.ProposalInput[](1);
        proposalInputs[0] = MTypes.ProposalInput({shorter: shorter, shortId: shortId, shortOrderId: 100}); // shortOrderId doesn't matter here
        depositUsd(account, redemptionAmount);

        vm.prank(account);
        diamond.proposeRedemption(asset, proposalInputs, redemptionAmount, MAX_REDEMPTION_FEE);
    }

    function test_oldOraclePrice() public {
        address seller = makeAddr("seller");
        address buyer = makeAddr("buyer");
        address randomRedemptioner = makeAddr("randomRedemptioner");

        // 1. make Short Record
        {
            fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, seller);
            fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, buyer);
        }

        {
            STypes.ShortRecord memory shortRecord = diamond.getShortRecord(asset, seller, 2);
            uint256 cr = diamond.getCollateralRatio(asset, shortRecord);
            console.log("Short Record CR after match", cr); // CR 6X
        }

        // 2. Time passes, price drops and protocol updates its price through any user action and CR becomes 1.90
        skip(10000);
        _setETH(1080 ether);

        {
            STypes.ShortRecord memory shortRecord = diamond.getShortRecord(asset, seller, 2);
            uint256 cr = diamond.getCollateralRatio(asset, shortRecord);
            console.log("Short Record CR after match", cr); // CR 1.62X
        }

        // 3. Time passes, no redemptions are made and the price goes back up
        skip(1600);
        _setETHChainlinkOnly(1500 ether);

        // # Toggle this - this will update the oracle price and save it in the protocol.
        // {
        // fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, buyer);
        // }

        // 4. CR is 2.25 on actual oracle price but redemption can be made against this position because the saved price makes the position
Risky debt accumulates, threatens system stability, solvency. || Timely liquidation hindered, unhealthy positions persist, system risks.
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/PrimaryLiquidationFacet.sol#L47-L90


# Vulnerability details

### Impact
   - If `minShortErc` is set too low:
     - The system may accumulate a significant amount of risky debt that is not being liquidated due to a lack of liquidator incentives.
     - This can pose a threat to the stability and solvency of the system, as the accumulated risky debt may become unmanageable.
   - If `minShortErc` is set too high:
     - The system may fail to liquidate risky ShortRecords in a timely manner, allowing unhealthy positions to persist and potentially worsen over time.
     - This can lead to a deterioration of the overall health of the system and increase the likelihood of a catastrophic failure.

### Description
   - The `minShortErc` parameter is used in the `PrimaryLiquidationFacet.sol` contract's `liquidate` function to ensure that a ShortRecord has a minimum amount of `ercDebt` to be eligible for liquidation.
   - https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/PrimaryLiquidationFacet.sol#L47-L90

     ```solidity
     function liquidate(address asset, address shorter, uint8 id, uint16[] memory shortHintArray)
         external
         isNotFrozen(asset)
         nonReentrant
         onlyValidShortRecord(asset, shorter, id)
         returns (uint88, uint88)
     {
         // ...
         LibSRUtil.checkCancelShortOrder({
             asset: asset,
             initialStatus: s.shortRecords[asset][shorter][id].status,
             shortOrderId: shortOrderId,
             shortRecordId: id,
             shorter: shorter
         });
         // ...
     }
     ```

   - The [LibSRUtil.checkCancelShortOrder](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/PrimaryLiquidationFacet.sol#L59-L65) is called to ensure that the ShortRecord's `ercDebt` is above `minShortErc` and to cancel the associated short order if necessary.

### Vulnerability Details
   - When the `minShortErc` parameter is not set appropriately.
   - If `minShortErc` is set too low:
     - Liquidators may not have sufficient incentive to liquidate risky debt because the potential caller fee they earn (based on the liquidated collateral) may not cover the costs and effort involved in the liquidation process.
     - This can lead to a situation where risky ShortRecords with low `ercDebt` remain unliquidated, allowing the debt to accumulate and potentially threatening the stability of the system.
   - If `minShortErc` is set too high:
     - Legitimate ShortRecords with `ercDebt` below the high threshold may not be eligible for liquidation, even if they pose a risk to the system.
     - This can prevent timely liquidation of risky debt and lead to a buildup of unhealthy positions in the system.


>  - When `minShortErc` is set too low, li
Disputants manipulate ShortRecord, claim undue penalties, disrupt redemption process.
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L259
https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L259
https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L259


# Vulnerability details

#### Vulnerability 1: Manipulated ShortRecord with Lower CR

_The dispute mechanism in the redemption process is vulnerable to manipulation by disputants who provide a ShortRecord with a lower CR that may not necessarily be the lowest available CR._

#### Description
   - `disputeRedemption` allows anyone to challenge a proposed redemption by providing a ShortRecord with a lower CR than the one being disputed.
   - The function retrieves the decoded proposal data, checks the validity of the dispute ShortRecord, and compares its CR with the CR of the incorrect proposal specified by the `incorrectIndex`.
   - If the dispute ShortRecord has a lower CR and its `updatedAt` timestamp is earlier than the proposal's `timeProposed`, the dispute is considered valid.

#### Vulnerability Details
   - Lies in the assumption that the disputer will provide a ShortRecord with the lowest available CR.
   - _But, a malicious disputer can manipulate a ShortRecord to have a lower CR than the disputed proposal, even if it is not the lowest available CR in the system._
>   - This manipulation can be achieved by artificially lowering the collateral or increasing the debt of a ShortRecord, making it appear to have a lower CR.
   - [RedemptionFacet.sol#259](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L259)
     ```solidity
     if (disputeCR < incorrectProposal.CR && disputeSR.updatedAt + C.DISPUTE_REDEMPTION_BUFFER <= redeemerAssetUser.timeProposed)
     ```

>  - The dispute mechanism is expected to allow disputants to provide a ShortRecord with the lowest available CR to challenge a proposed redemption.
>  - The purpose is to ensure that the redemption process progresses from the lowest CR to the highest CR and prevents any incorrect or out-of-order redemptions.


   - _A malicious disputer can provide a manipulated ShortRecord with a lower CR that is not necessarily the lowest available CR._
   - _This allows the disputer to successfully dispute a redemption proposal even if there are other ShortRecords with lower CRs that should have been considered first._
   - _As a result, the disputer can claim undue penalties and disrupt the integrity of the redemption process._

## Impact
   - It allows malicious disputants to game the system and claim undue penalties, potentially leading to financial losses for honest redeemers.
   - The integrity of the redemption mechanism is compromised, as it may not always progress from the lowest CR to the highe
Stale cached prices may misprice assets, delay liquidations, and increase risk.
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibOracle.sol#L22
https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibOracle.sol#L19-L67


# Vulnerability details

If the market experiences significant price volatility within the 15-minute window, the cached price may diverge from the actual market price. _This discrepancy can lead to several issues:_

- Incorrect calculation of collateral ratios:
  - If the cached price is higher than the actual market price, undercollateralized positions may remain open, exposing the protocol to increased risk.
  - If the cached price is lower than the actual market price, positions may be prematurely liquidated, causing unnecessary losses for users.

- Mispricing of assets during order matching:
  - If the cached price is stale, orders may be matched at prices that do not reflect the current market conditions.
  - This can result in unfair trades, where one party may receive an advantageous price at the expense of the other.
  - Malicious actors may exploit this by manipulating the market during the cache duration to benefit from the stale prices.

- Delayed liquidations of risky positions:
  - If the cached price is higher than the actual market price, positions that have fallen below the liquidation threshold may not be promptly identified and liquidated.
  - This delay in liquidation can expose the protocol to increased risk and potential losses.

It's from the following: [LibOracle.sol#L22](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibOracle.sol#L22)

```solidity
uint256 protocolPrice = getPrice(asset);
// ... Oracle price retrieval logic ...
```
The `getPrice` function retrieves the cached price, which may be stale if the cache duration has not expired.

## Impact
- Users may suffer financial losses due to incorrect liquidations or unfair pricing during order matching.
- The protocol may be exposed to increased risk if undercollateralized positions are not promptly identified and liquidated.

## Description
`getOraclePrice` is responsible for retrieving the asset price from the oracle. It first attempts to fetch the price from the Chainlink oracle and falls back to a TWAP price from Uniswap if the Chainlink price is invalid or outdated.

[LibOracle.sol#getOraclePrice](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibOracle.sol#L19-L67)

```solidity
function getOraclePrice(address asset) internal view returns (uint256) {
    AppStorage storage s = appStorage();
    AggregatorV3Interface baseOracle = AggregatorV3Interface(s.baseOracle);
    uint256 protocolPrice = getPrice(asset);        <---------@

    AggregatorV3Interface oracle = AggregatorV3Interface(s.asset[asset].oracle);           <---------@
    if (address(oracle) ==
The `colRedeemed` variable is wrongly retrieved in `LibBytes::readProposalData` function
Audit: 2024-03-dittoeth-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibBytes.sol#L42


# Vulnerability details

## Impact
The `LibBytes::readProposalData` function uses inline assembly for efficient data extraction from a byte array. The `colRedeemed` variable, which represents an 11-byte value within the `ProposalData` structure, is intended to be extracted by applying a mask to isolate the relevant bytes. However, the current implementation incorrectly uses the `add` operation. That leads to retrieve incorrect value of `colRedeemed` variable:

```javascript

    function readProposalData(address SSTORE2Pointer, uint8 slateLength) internal view returns (MTypes.ProposalData[] memory) {
        bytes memory slate = SSTORE2.read(SSTORE2Pointer);
        // ProposalData is 51 bytes
        require(slate.length % 51 == 0, "Invalid data length");

        MTypes.ProposalData[] memory data = new MTypes.ProposalData[](slateLength);

        for (uint256 i = 0; i < slateLength; i++) {
            // 32 offset for array length, mulitply by each ProposalData
            uint256 offset = i * 51 + 32;

            address shorter; // bytes20
            uint8 shortId; // bytes1
            uint64 CR; // bytes8
            uint88 ercDebtRedeemed; // bytes11
            uint88 colRedeemed; // bytes11

            assembly {
                // mload works 32 bytes at a time
                let fullWord := mload(add(slate, offset))
                // read 20 bytes
                shorter := shr(96, fullWord) // 0x60 = 96 (256-160)
                // read 8 bytes
                shortId := and(0xff, shr(88, fullWord)) // 0x58 = 88 (96-8), mask of bytes1 = 0xff * 1
                // read 64 bytes
                CR := and(0xffffffffffffffff, shr(24, fullWord)) // 0x18 = 24 (88-64), mask of bytes8 = 0xff * 8

                fullWord := mload(add(slate, add(offset, 29))) // (29 offset)
                // read 88 bytes
                ercDebtRedeemed := shr(168, fullWord) // (256-88 = 168)
                // read 88 bytes
@>              colRedeemed := add(0xffffffffffffffffffffff, shr(80, fullWord)) // (256-88-88 = 80), mask of bytes11 = 0xff * 11
            }

            data[i] = MTypes.ProposalData({
                shorter: shorter,
                shortId: shortId,
                CR: CR,
                ercDebtRedeemed: ercDebtRedeemed,
                colRedeemed: colRedeemed
            });
        }

        return data;
    }

```


The `add` operation would incorrectly add the mask to the shifted value, potentially resulting in an incorrect value for `colRedeemed`. The correct operation should use `and` to apply the mask and isolate the 11-byte `colRedeemed` value.

The `RedemptionFacet` contract calls the `LibBytes::readProposalData` function and uses `colRedeemed` variable in `claimRedemption` function.


## Proof of Concept
Link to the code: https://github.com/code-423n4/2024-03-dittoeth