xanoutas/solidity-security-findings huggingface dataset:
Attacker can force pause the AuctionHouse contract.
Audit: 2023-12-revolutionprotocol-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/AuctionHouse.sol#L309-L330


# Vulnerability details

## Impact
An attacker can potentially exploit the try catch within the `AuctionHouse._CreateAuction()` function to arbitrarily pause the auction contract.

## Proof of Concept
This is the same [issue](https://code4rena.com/reports/2022-09-nouns-builder#m-15-malicious-pausing-the-contract) reported in a similar codebase. The addition of the gas check does not prevent it from being exploited the same way, as shown in the coded POC below.

Once an auction ends, anyone can call `AuctionHouse.settleCurrentAndCreateNewAuction()` to settle the finished auction and create a new one. To start a new auction, the system initially attempts to mint a new token from `VerbsTokens`. In case the minting fails, the corresponding `catch` branch gets triggered, pausing the auction contract.

Currently on Ethereum and EVM-compatible chains, calls can consume at most 63/64 of the parent's call gas (See [EIP-150](https://eips.ethereum.org/EIPS/eip-150)). An attacker can exploit this circumstances of high gas cost to restrict the parent gas call limit, making `verbs.mint()` fail and still leaving enough gas left (1/64) for the `_pause()` call to succeed. Therefore he is able to force the pausing of the auction contract. Note that the gas check (`require(gasleft() >= MIN_TOKEN_MINT_GAS_THRESHOLD, "Insufficient gas for creating auction");`) does not prevent this exploit, as the attacker would need to call with more than 750000 to pass the above requirement of 1/64.

For the attack to be possible the minting would have to consume huge amounts of gas, much more than it would consume on a "normal" mint. An attacker can fulfill those conditions by creating art pieces with `metadata` containing long strings, so that minting takes lots of gas due to the excessive storage costs. As the `metadata` field on new art pieces is user controlled and there is no check for strings length, an attacker can craft custom `metadata` so that `verbs.mint()` will consume as much gas as he desires.

So the attack would proceed as follows.
1. Attacker create new art piece with his crafted `metadata`.

If his art piece ever becomes the most voted one, either because the attacker controls a significant portion of all votes or normally by being voted by others members of the community, then the attack can proceed.

2. Attacker calls `AuctionHouse.settleCurrentAndCreateNewAuction()`, restricting the maximum gas to cause the revert during minting.

As explained above, his art would fail to be minted due to consuming more gas then the 63/64 availiable to it, thus triggering the `catch` branch and pausing the auction. See the coded POC below for a simulation of this scenario.

Exploit contract code:
```solidity
pragma solidity ^0.8.16;

contract Attacker {
    function forcePause(address target) external {
Attacker can profit from discount fees
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/OrdersFacet.sol#L132-L180


# Vulnerability details


## Impact
It is possible that the amount of dUSD minted in discount fees are greater than the discount loss. An attacker can therefore deliberately trigger a fee and, provided he has a large stake in the yDUSD vault, he can claim more dUSD in fees, than lost from the trade.
The root cause is that the discount fee is fixed in proportion only to the entire debt.

## Proof of Concept
Suppose dUSD trades at the intended 1:1 peg. The effective loss from placing an order at the price 0.95 is then `0.05 * ercAmount`.
Since the difference in price is >1% the discount fee kicks in.
`discountPct` is first calculated to `0.05`. Then, after applying the `discountMultiplier` (10 by default) it will be `0.5 * daysElapsed`.
If sufficiently many days have passed the `discount` will thus be an arbitrarily high number `k`.
`pctOfDiscountedDebt` will be `k * ercAmount / ercDebt`. This must be `>0.01` for the fee to be applied. I.e. `ercAmount = 0.01 * ercDebt / k` (or slightly more) is sufficient.
The loss is then `0.0005 * ercDebt / k`.

The `discountPenaltyFee` is 0.1% and is applied to (almost) the entire `ercDebt`. This, `0.001 * ercDebt` is the amount dUSD minted.

The question is then whether the loss `0.0005 * ercDebt / k` can be smaller than the `0.001 * ercDebt` minted. This will happen if `k > 0.5`, for which two days is sufficient.

```solidity
function _matchIsDiscounted(MTypes.HandleDiscount memory h) external onlyDiamond {
    STypes.Asset storage Asset = s.asset[h.asset];
    uint32 protocolTime = LibOrders.getOffsetTime();
    Asset.lastDiscountTime = protocolTime;
    // @dev Asset.initialDiscountTime used to calculate multiplier for discounts that occur nonstop for days (daysElapsed)
    if (Asset.initialDiscountTime <= 1 seconds) {
        // @dev Set only during first discount or when discount had been previously reset
        Asset.initialDiscountTime = protocolTime;
    }
    // @dev Cap the discount at 5% to prevent malicious attempt to overly increase ercDebt
    uint256 discountPct = LibOrders.min((h.savedPrice - h.price).div(h.savedPrice), 0.05 ether);
    // @dev Express duration of discount in days
    uint32 timeDiff = (protocolTime - Asset.initialDiscountTime) / 86400 seconds;
    uint32 daysElapsed = 1;
    if (timeDiff > 7) {
        // @dev Protect against situation where discount occurs, followed by long period of inactivity on orderbook
        Asset.initialDiscountTime = protocolTime;
    } else if (timeDiff > 1) {
        daysElapsed = timeDiff;
    }
    // @dev Penalties should occur more frequently if discounts persist many days
    // @dev Multiply discountPct by a multiplier to penalize larger discounts more
    discountPct = (discountPct * daysElapsed).mul(LibAsset.discountMultiplier(Asset));
    uint256 discount = 1 ether + discountPct;
    Ass
Inflation of yDUSD vault
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/tokens/yDUSD.sol#L9


# Vulnerability details



## Impact
Inflation attack. Griefing on one deposit, profit for attacker on two or more deposits.

## Proof of Concept
The yDUSD vault, inheriting from OpenZeppelin's ERC4626, is vulnerable to an inflation attack. The virtual share only protects against targeting a single depositor for his entire deposited amount. Two deposits following the malicious donation can still bring a profit to the attacker. See [this report](https://github.com/code-423n4/2024-04-noya-findings/issues/1328) for details on such a double deposit attack.

Also, because of this virtual share, if discount fees are minted to the vault before anyone has deposited, this is in itself a donation that causes rounding errors. These will be lost to the vault (to the virtual share).

## Recommended Mitigation Steps
Make an initial deposit, or increase the decimal offset.


## Assessed type

ERC4626
Bypassing discount sale penalties 
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/BidOrdersFacet.sol#L338
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L659


# Vulnerability details

## Impact

When matching an order, the system checks if it has been sold below market price in order to apply penalties for all shorters. This is checked in `BidOrdersFacet#L338` and `LibOrders#659`.

```solidity
File: BidOrdersFacet.sol
337:         // @dev match price is based on the order that was already on orderbook
338:         LibPriceDiscount.handlePriceDiscount(asset, matchTotal.lastMatchPrice, matchTotal.fillErc);
339:         return (matchTotal.fillEth, incomingBid.ercAmount);

```solidity
File: LibOrders.sol
658:         // @dev match price is based on the order that was already on orderbook
659:         LibPriceDiscount.handlePriceDiscount(asset, matchTotal.lastMatchPrice, matchTotal.fillErc);
660:     }
```

The problem lies in sending to the `LibPriceDiscount::handlePriceDiscount` function the last price of an match order (`matchTotal.lastMatchPrice`). By manipulating the **last match price** to match the oracle price, a user can bypass the intended penalties. This process allows for discounted sales without penalties.

## Proof of Concept

The following test demonstrates how a sale is made at a price below the oracle price, thereby avoiding penalties for this.

1. A short order is created for `ercAmount=DEFAULT_AMOUNT * 10_000` below the oracle price. It is added to [the `OrderBook`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/ShortOrdersFacet.sol#L90). 
2. A short order is created for `ercAmount=DEFAULT_AMOUNT` at the oracle price.
3. A bid order is created that covers the orders from step 1 `ercAmount=DEFAULT_AMOUNT * 10_000` and delays the order from `step 2` until the end, thus evaluating the last short order created at the oracle price.

```solidity
    function test_handleDiscountBypass() public {
        // amount = DEFAULT_AMOUNT * 10_000
        fundLimitShortOpt(DEFAULT_PRICE, amount, sender);
        fundLimitBidOpt(DEFAULT_PRICE, amount, receiver);
        fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, sender); // OrderBook
        fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT / 2, receiver);
        STypes.ShortRecord memory shortRecord = getShortRecord(sender, C.SHORT_STARTING_ID);
        STypes.ShortRecord memory extraSR = getShortRecord(extra, C.SHORT_STARTING_ID);
        assertEq(shortRecord.ercDebt, amount);
        assertEq(token.balanceOf(_yDUSD), 0);
        //
        // 1. Create short at lower price. amount = DEFAULT * 10_000
        (uint80 lowShortPrice,) = getPrices(0.96 ether);
        fundLimitShortOpt(lowShortPrice, amount, extra);
        // short at oraclePrice
        fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, extra);
Minting ERC at an outdated price in redemption stage
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/ProposeRedemptionFacet.sol#L104
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L946


# Vulnerability details

## Impact

A malicious user or an unintentional action could lead to minting ERC at an outdated price. This can happen in the following steps:

1. A proposal is created for a Short Record (SR) with `partialFill`, resulting in [`ercDebt=0`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/ProposeRedemptionFacet.sol#L104).
2. The shorter of the partially filled SR cancels the associated short.
3. Subsequently, the user [mints `minShortErc`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L975) at a price that is not updated to the Oracle's current price.

As a result, the `shortRecord` can become sub-collateralized or under-collateralized depending on the current Oracle price. This issue can occur by chance when an SR is in the redemption stage and the short is closed or it can be done deliberately by malicious user to mint ERC at a favorable price (free dUSD).

Additionally, when the `shortRecord.CR` is lower than the initial, [`ethInitial` is also deposited](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/ShortOrdersFacet.sol#L56-L62) to cover the `minShortErc`, therefore, the user would be charged the `minShortErc` amount twice.

## Proof of Concept

The following test demonstrates how a shorter can close an order in the redemption process, resulting in ERC being minted at an outdated price.

```solidity
    function test_mintERCAtOutdatedPrice() public {
        //
        fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, receiver);
        fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, sender);
        // PartiallFill order
        fundLimitBidOpt(DEFAULT_PRICE + 1, DEFAULT_AMOUNT + 1, receiver);
        fundLimitShortOpt(DEFAULT_PRICE + 1, DEFAULT_AMOUNT * 2, extra);
        //
        // 1. Propose orders
        uint88 redemptionAmount = DEFAULT_AMOUNT * 2;
        MTypes.ProposalInput[] memory proposalInputs = new MTypes.ProposalInput[](2);
        proposalInputs[0] = MTypes.ProposalInput({shorter: extra, shortId: C.SHORT_STARTING_ID, shortOrderId: 101});
        proposalInputs[1] = MTypes.ProposalInput({shorter: sender, shortId: C.SHORT_STARTING_ID, shortOrderId: 0});
        address redeemer = receiver;
        depositUsd(redeemer, DEF_REDEMPTION_AMOUNT);
        _setETH(1000 ether);
        vm.prank(redeemer);
        diamond.proposeRedemption(asset, proposalInputs, redemptionAmount, MAX_REDEMPTION_FEE, MAX_REDEMPTION_DEADLINE);
        //
        // 2. `extra user` cancel short and virtually mints ERC at short price. SR Collat
`DUSD` assets can be minted with less `ETH` collateral than required
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/OrdersFacet.sol#L60
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L944
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L946
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L953
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L960


# Vulnerability details

## Summary

> I discovered that the current implementation has not fixed the issue [H-03 (Users can mint DUSD with less collateral than required which gives them free DUSD and may open a liquidatable position)](https://github.com/code-423n4/2024-03-dittoeth-findings/issues/134) raised in the previous C4 audit.

## Description

To mint the `DUSD` assets with less collateral than required, a user or attacker executes the `OrdersFacet::cancelShort()` to cancel the `shortOrder` with its `shortRecord.ercDebt` < `minShortErc` (i.e., `shortRecord.status` == `SR.PartialFill`).

The `OrdersFacet::cancelShort()` will [execute another internal function, `LibOrders::cancelShort()`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/OrdersFacet.sol#L60) (`@1` in the snippet below), to do the short canceling job. If the `shortOrder`'s corresponding `shortRecord.status` == `SR.PartialFill` and has `shortRecord.ercDebt` < `minShortErc`, the steps [`@2.1`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L944) and [`@2.2`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L946) will get through. 

Since the `shortRecord` is less than the `minShortErc`, the `cancelShort()` has to fill an `ercDebt` for more to reach the `minShortErc` threshold (so that the partially filled `shortRecord.ercDebt` will == `minShortErc`). Specifically, the function has to virtually mint the `DUSD` assets to increase the `ercDebt` by spending the `shortRecord.collateral` (Let's name it the `collateralDiff`) for exchange.

Here, we come to the root cause in [`@3`](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L953). To calculate the `collateralDiff`:
1. The `shortOrder.price` is used instead of the current price. Nevertheless, the `shortOrder.price` can be stale (less or higher than the current price).
2. The `shortOrder.shortOrderCR` (i.e., the `cr` variable in the snippet below) is used, which can be less than 100% CR.

If the `shortOrder.price` is less than the current price and/or the `shortOrder.shortOrde
Incorrect accounting bug of the `yDUSD` vault leads to total loss of depositors' `DUSD` assets
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/OrdersFacet.sol#L178
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/tokens/yDUSD.sol#L69
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/tokens/yDUSD.sol#L72
https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/tokens/yDUSD.sol#L77
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4fd2f8be339e850c32206342c3f9a1a7bedbb204/contracts/token/ERC20/extensions/ERC4626.sol#L134
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4fd2f8be339e850c32206342c3f9a1a7bedbb204/contracts/token/ERC20/extensions/ERC4626.sol#L200
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4fd2f8be339e850c32206342c3f9a1a7bedbb204/contracts/token/ERC20/ERC20.sol#L94
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4fd2f8be339e850c32206342c3f9a1a7bedbb204/contracts/token/ERC20/extensions/ERC4626.sol#L99


# Vulnerability details

## Summary

The current implementation of the `yDUSD` vault does not properly support the auto-compounding token rewards mechanism by directly minting the `DUSD` assets to the vault. 

Due to an incorrect accounting bug of the vault's total supply (shares), depositors can lose some deposited `DUSD` assets (principal) or even all the assets when the `Ditto` protocol mints `DUSD` debts to the vault to account for any discounts.

## Description

When the match price is below the oracle price, the `OrdersFacet::_matchIsDiscounted()` will be invoked to account for a discount [by minting the discount (`newDebt`)](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/OrdersFacet.sol#L178) (`@1` in the snippet below) to the `yDUSD` vault.

Assuming the `yDUSD` vault is empty (i.e., both `totalAssets` and `totalSupply` are 0), for simplicity's sake. This step will increase the vault's total `DUSD` assets (`totalAssets` -- spot balance) without updating the vault's total supply (`totalSupply` -- tracked shares).

```solidity
    function _matchIsDiscounted(MTypes.HandleDiscount memory h) external onlyDiamond {
        ...

        if (pctOfDiscountedDebt > C.DISCOUNT_THRESHOLD && !LibTStore.isForcedBid()) {
            ...

            // @dev Increase global ercDebt to account for the increase debt owed by shorters
            uint104 newDebt = uint104(ercDebtMinusTapp.mul(discountPenaltyFee));
            Asset.ercDebt += newDebt;
            Asset.ercDebtFee += uint88(newDebt); // should be uint104?

            // @dev Mint dUSD to the yDUSD vault for
            // Note: Does not currently handle mutli-asset
@1          IERC20(h.asset).mint(s.yieldVault[h.asset], newDebt);
                //@audit @1 -- When the match price is below the oracle price, the _matchIsDiscounted()
An attacker can mint free DUSD and liquidate the corresponding Short Record to earn liquidation rewards
Audit: 2024-07-dittoeth-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/facets/ShortRecordFacet.sol#L83-L107


# Vulnerability details

## Summary
An attacker can use `decreaseCollateral()` function to reduce the collateral of a Short Recordn (SR) and `cancelOrder()` to cancel the corresponding Short Order having a collateral ratio < 1. The Short Record ends up having less collateral than debt and is now liquidatable.

## Description
A user can create a Short Order with a collateral ratio (CR) of less than 1. The protocol ensures that at least `minShortErc` in the Short Orders Short Record has enough collateral by filling the Short Record with collateral to cover `minShortErc` before the Short Order is matched or placed on the order book. 

In addition, during the match the bid also provides equivalent collateral to the `ercDebt` it creates. So if the Short Order CR is 0.7 it ends up being 1.7 CR after matching.

By utilising the `cancelShort()` and `decreaseCollateral()` functions on a short Order with CR < 1, an attacker can create free DUSD and make the Short Record liquidatable. 

The [cancelShort()](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L923-L995) function lets the caller cancel a Short Order. If the `ercDebt` is smaller than `minShortErc` as shown in line 953 below, it mints the difference to the shorter (i.e the address that created the Short Order) in line 982. The amount of collateral needed is calculated in line 960 and uses `cr`. `cr` is the collateral ratio and if it is less than 1 then the protocol would be minting DUSD with lesser collateral. But since it has already ensured that `minShortErc` has enough collateral in the Short Record before the Short Order was created, this shouldn't be a problem.

**[LibOrders.sol#L946-L976](https://github.com/code-423n4/2024-07-dittoeth/blob/ca3c5bf8e13d0df6a2c1f8a9c66ad95bbad35bce/contracts/libraries/LibOrders.sol#L946-L976)**
```solidity
953:             if (shortRecord.ercDebt < minShortErc) {
954:                 // @dev prevents leaving behind a partially filled SR under minShortErc
955:                 // @dev if the corresponding short is cancelled, then the partially filled SR's debt will == minShortErc
956:                 uint88 debtDiff = uint88(minShortErc - shortRecord.ercDebt); // @dev(safe-cast)
957:                 {
958:                     STypes.Vault storage Vault = s.vault[vault];
959:                     // @audit-issue the collateral could be bad if price moved.
960:                     uint88 collateralDiff = shortOrder.price.mulU88(debtDiff).mulU88(cr);
961: 
962:                     LibShortRecord.fillShortRecord(
963:                         asset,
964:                         shorter,
965:                         shortRecordId,
966:                         SR.FullyFilled,
967:                         collateralDiff,
968: