xanoutas/solidity-security-findings huggingface dataset:
No token withdrawal/revocation; add function for withdrawal and approval revocation.
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63


# Vulnerability details

## Impact

There is no way for a `delegator` to revoke approval or withdraw from a delegate. Once tokens are delegated, they are permanently locked to that delegate.

Some functions should be added to allow withdrawing delegation.

## Proof of Concept

The issue here is that there is no functionality to revoke approval or withdraw tokens once they have been delegated.

**[delegateMulti](https://github.com/code-423n4/2023-10-ens/blob/ed25379c06e42c8218eb1e80e141412496950685/contracts/ERC20MultiDelegate.sol#L57-L63)**

The core issue is that once tokens are delegated via the `delegateMulti` function, there is no way for the delegator to get them back:
```solidity
function delegateMulti(
  // ...
) external {
  // Delegate tokens
  _delegateMulti(sources, targets, amounts); 
}
```
Once `delegateMulti` has delegated tokens to the target delegates by calling `_delegateMulti`, there is no way for the delegator to get their tokens back or revoke approval. 

Specifically:

- `delegateMulti` calls `_delegateMulti` to delegate tokens to target addresses
- This transfers the tokens from the delegator's proxy to the delegate's proxy
- The delegated tokens are now locked in the delegate's proxy contract

There is no functionality to:

- Allow the delegator to withdraw their tokens from a delegate
- Revoke the delegate's approval to transfer the delegator's tokens

This means delegated tokens are permanently locked to the delegate address.

## Tools Used

Manual Review

## Recommended Mitigation Steps

A `withdrawFromDelegate` function could be added like:

```solidity
function withdrawFromDelegate(address targetDelegate) external {

  // Get proxy address for delegator and delegate
  address delegatorProxy = getProxyForDelegator(msg.sender);
  address delegateProxy = getProxyForDelegate(targetDelegate);

  // Withdraw tokens from delegate proxy
  uint amount = getBalanceForDelegate(targetDelegate);
  token.transferFrom(delegateProxy, delegatorProxy, amount);

  // Revoke delegate proxy approval
  token.approve(delegateProxy, 0);

}
```

This allows a delegator to withdraw their tokens and revoke approval for a delegate's proxy.




## Assessed type

Governance
Token ids can differ from the delegatee address
Audit: 2023-10-ens-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L57-L61


# Vulnerability details

## Summary

The entrypoint interface allows users to delegate to sources outside the address space, causing issues when the ERC1155 token id doesn't match the delegatee address.

## Impact

In ERC20MultiDelegate, delegations are represented by an ERC1155 token. When a user delegates voting power through the protocol, an ERC1155 token with an id matching the address of the delegatee is minted based on the number of votes being delegated.

Since ERC1155 token ids are `uint256`, the main entrypoint receives both sources and targets addresses as `uint256`, which are then casted to `address` internally.

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L57-L61

```solidity
57:     function delegateMulti(
58:         uint256[] calldata sources,
59:         uint256[] calldata targets,
60:         uint256[] calldata amounts
61:     ) external {
```

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L90-L95

```solidity
90:             address source = transferIndex < sourcesLength
91:                 ? address(uint160(sources[transferIndex]))
92:                 : address(0);
93:             address target = transferIndex < targetsLength
94:                 ? address(uint160(targets[transferIndex]))
95:                 : address(0);
```

The issue here is that the input types have a bigger space than the address type. It is possible to send a source argument with a value different from the intended address, but such that when downcasted (note the downcast is not a safe operation here) the address is valid.

This will have the effect of having an ERC1155 token with an id different from the address of the delegatee. Among its impact, and further elaborated in the proof of concept, this will cause issues while transferring the delegation. When delegations are moved between a source and a target, the ERC1155 balance check is done using the address, which is then upcasted to a `uint256`:

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L124-L131

```solidity
124:     function _processDelegation(
125:         address source,
126:         address target,
127:         uint256 amount
128:     ) internal {
129:         uint256 balance = getBalanceForDelegate(source);
130: 
131:         assert(amount <= balance);
```

https://github.com/code-423n4/2023-10-ens/blob/main/contracts/ERC20MultiDelegate.sol#L192-L196

```solidity
192:     function getBalanceForDelegate(
193:         address delegate
194:     ) internal view returns (uint256) {
195:         return ERC1155(this).balanceOf(msg.sender, uint256(uint160(delegate)));
196:     }
```

Since the token id doesn't match the source address, the balance will be zero, and the call will revert in line 131.

## Proof of Concept

In the following test, the user delegates their voti
A user having a bad collateral ratio can just transfer their short to sidestep being liquidated
Audit: 2024-03-dittoeth-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details


## Proof of Concept

Take a look at https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibSRUtil.sol#L124-L150

```solidity
    function transferShortRecord(address from, address to, uint40 tokenId) internal {
        AppStorage storage s = appStorage();

        STypes.NFT storage nft = s.nftMapping[tokenId];
        address asset = s.assetMapping[nft.assetId];
        STypes.ShortRecord storage short = s.shortRecords[asset][from][nft.shortRecordId];
        if (short.status == SR.Closed) revert Errors.OriginalShortRecordCancelled();
        if (short.ercDebt == 0) revert Errors.OriginalShortRecordRedeemed();

        // @dev shortOrderId is already validated in mintNFT
        if (short.status == SR.PartialFill) {
            LibOrders.cancelShort(asset, nft.shortOrderId);
        }

        short.tokenId = 0;
        LibShortRecord.deleteShortRecord(asset, from, nft.shortRecordId);
        LibBridgeRouter.transferBridgeCredit(asset, from, to, short.collateral);

        uint8 id = LibShortRecord.createShortRecord(
            asset, to, SR.FullyFilled, short.collateral, short.ercDebt, short.ercDebtRate, short.dethYieldRate, tokenId
        );

        nft.owner = to;
        nft.shortRecordId = id;
        nft.shortOrderId = 0;
    }

```

The `transferShortRecord` function in the provided code snippet allows for the transfer of short records as NFTs (ERC-721) between addresses. However, it introduces a vulnerability where any user can use this as a method to sidestep liquidations, this is cause while transferring the short records there are no checks that the position is currently above liquidatable grounds, i.e a user having a ShortRecord with low collateral ratio can just transfer it to another of their own address by front running aliquidation. Since the [liquidator in this case has to pass both the shorter and the ids to specify the short](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/PrimaryLiquidationFacet.sol#L47-L55), any attempt to liquidate the short will fail.

## Impact

This leads to bad collateral ratio for the system since liquidations can be easily sidestepped.

## Recommended Mitigation Steps

While transferring a short record, check if the collateral ratio is below the primary liquidation threshold and then revert on instances like this



## Assessed type

Context
Oracle price of ETH/dUSD is used instead of ETH/USD in redemptions
Audit: 2024-03-dittoeth-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details



## Impact
dUSD is not redeemed at 1 USD/dUSD as intended, but simply traded for ETH at its current price. This completely disincentivizes redemptions, which are necessary to maintain the peg.

## Proof of Concept
When a redemption is claimed [the number `colRedeemed` for each short record is the amount of ETH the redeemer gets](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L331). `colRedeemed` is calculated as [`colRedeemed = amountProposed * oraclePrice`](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L116), where `amountProposed` is in units of dUSD. The intention is that `amountProposed` dUSD should redeem `amountProposed` USD worth of ETH, i.e. as if `USD/dUSD = 1`. This means that `oraclePrice` should be the price of ETH/USD. But [`oraclePrice` is `LibOracle.getPrice(p.asset)`](https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/RedemptionFacet.sol#L75) where `asset` is dUSD, i.e. `oraclePrice` is the price ETH/dUSD.
This means that dUSD is redeemed at the current value of dUSD, instead of at the peg of 1:1 dUSD:USD. This is the equivalent of just trading dUSD for ETH, instead of a redemption for an undervalued dUSD at its pegged valued.

## Recommended Mitigation Steps
Set `oraclePrice` to the price ETH/USD.


## Assessed type

Oracle
A successfully disputed redemption proposal has still increased the redemption fee base rate; exploit to depeg dUSD
Audit: 2024-03-dittoeth-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details




## Impact
Redemptions may not be incentivized to increase the value of dUSD. Furthermore, this may be deliberately induced to prevent a falling dUSD from repegging, ultimately causing dUSD to fully depeg and crash.
The motivations for this attack could be anything from pure spite (perhaps from a competitor) 

## Root cause and summary
The most fundamental root cause of this issue and exploit is that the base rate in the redemption fee is increased when a redemption is proposed but not restored (decreased) if the redemption is successfully disputed.
When dUSD is trading below the dollar, redemptions are needed to restore the peg. The redemption fee regulates the incentive to redeem dUSD. So if the redemption fee is increased, without contributing to the peg restoration by decreasing the supply of dUSD, the peg restoration mechanism is impeded and sufficient redemptions will not happen. A sufficiently high redemption fee will make any redemption a loss for the redeemer, which means that the peg will not be restored.
This issue becomes viable as a direct exploit because of multiple factors reducing its cost to the attacker. These are
1. The attacker can avoid paying the penalty by disputing his own proposal (using a different account).
2. The redemption fee contains an added base fee of 0.5% which reduces the required amount to propose, on which the fee is paid.
3.  a. The total redemption fee decreases, down to almost a half, if the proposed amount is split over several redemption proposals.
    b. By splitting the total proposed amount the attacker only needs to escrow dUSD for one such part (at a time), which minimizes his own loss on his devalued dUSD.
4. The redemption fee is proportional to the collateral that would be redeemed, which is capped to the short record's collateral balance, whereas the base rate is increased based on the amount proposed, which is not reduced accordingly. This can only be leveraged if there exists an undercollateralized short record. But if there is, this can be leveraged to make this exploit essentially for free for about 48 hours.

With the possible exception of 1. all of the above are issues in their own right, especially 4. which is therefore also reported separately. The impact of 2. and 3. is subject to the model used for how (quantitatively) redemptions restore the peg, and therefore discussed below rather than reported separately.

## Proof of Concept
The goal of this exploit is to cause a persistent and complete loss of incentive to redeem dUSD, such that a dUSD trading below the dollar fails to recover its peg.
There is some level below which dUSD is critically depegged. This is at least at the level where it becomes undercollateralized. For example, if the total collateral ratio is 1.25, this would happen if dUSD trades at $0.
Race condition in exitShortFacet::exitShortErcEscrowed could refunding the full collateral
Audit: 2024-03-dittoeth-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When the original `exitShortErcEscrowed` function from User A resumes, it will use the outdated values for ercDebt and collateral, potentially leading to inconsistencies or unexpected behavior.

For example, the function may incorrectly refund the full collateral amount if the ercDebt is now less than the buybackAmount, even though the ercDebt was modified by User B's transaction.

## Proof of Concept
`https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/facets/ExitShortFacet.sol#L87C5-L130C1`

User A calls the exitShortErcEscrowed function with a valid buybackAmount.
The function first updates the ercEscrowed value, reducing it by the buybackAmount.
Before the function can update the ercDebt and collateral values, another transaction, e.g., from User B, modifies the ercDebt or collateral values for the same asset and user.
When the original exitShortErcEscrowed function resumes, it will use the outdated values, potentially leading to inconsistencies or unexpected behavior.

OR consider this scenario:

User A calls the exitShortErcEscrowed function with the following parameters:

asset: 0x1234...
id: 1
buybackAmount: 100
shortOrderId: 123

The function first updates the ercEscrowed value for the user's asset:

`s.assetUser[asset][msg.sender].ercEscrowed` is reduced by 100.

Before the function can update the ercDebt and collateral values, a malicious user, User B, calls another function that modifies the ercDebt or collateral values for the same asset and user.

For example, User B calls the `updateShortPosition` function with the following parameters:

asset: 0x1234...
id: 1
newCollateral: 200
newErcDebt: 150
This updates the `collateral` and `ercDebt` values for the same asset and user.

When the original `exitShortErcEscrowed` function from User A resumes, it will use the outdated values for `ercDebt` and `collateral`, potentially leading to inconsistencies or unexpected behavior.

The function may incorrectly refund the full collateral amount if the `ercDebt` is now less than the `buybackAmount`, even though the `ercDebt` was modified by User B's transaction.

## Tools Used
Manual Review

## Recommended Mitigation Steps
To mitigate this race condition, the function should perform all the state updates (ercEscrowed, ercDebt, and collateral) within a single transaction or use appropriate locking mechanisms to ensure atomicity and prevent race conditions.


## Assessed type

Timing
User cant withdraw ETH if user enter more than ethEscrowed in withdraw function  
Audit: 2024-03-dittoeth-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
when user try to withdraw eth from the bridge and if user enters dethamount as more than eth Escrowed and user has also credit balance in other bridge then it reverts. 
There is a logical error in the line 
instance-1:
https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibBridgeRouter.sol#L63
instance-2:
https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibBridgeRouter.sol#L93

Every time user enter deth amount more than then the transaction is going to revert.
 
## Proof of Concept
In withdraw function in BridgeRouter.sol , if Vault == Vault.One then it calls for the function assessDeth function (which is gives information about eth which is not covered by the bridge credit).
In assessDeth function, when deth is more then ethEscrowed in one bridge then it checks for the ethEscrowed in other bridge 
code :
            if (creditReth >= amount) {
                VaultUser.bridgeCreditReth -= amount;
                return 0;
            }

            VaultUser.bridgeCreditReth = 0;
            amount -= creditReth;
            creditSteth = VaultUser.bridgeCreditSteth;
            if (creditSteth < C.ROUNDING_ZERO) {
                // Valid withdraw when no STETH credits
                return amount;
            } else {
                if (IBridge(stethBridge).getDethValue() < C.ROUNDING_ZERO) // Validation ERROR
                     if (creditSteth >= amount) {
                        VaultUser.bridgeCreditSteth -= amount;
                        return 0;
                    } else {
                        VaultUser.bridgeCreditSteth = 0;
                        return amount - creditSteth;
                    }
                } else {
                    // Must use available bridge credits on withdrawal
                    // @dev Prevents abusing bridge for arbitrage
                    revert Errors.MustUseExistingBridgeCredit();
                }
In the  line 11 there is a logical error. there always exist eth on the stethBridge which is more than the C.ROUNDING_ZERO so , else block is executed every time user enters deth amount greater than the bridge credits in one bridge and also , if user has other bridge credits.
if user has enough steth credit and after it checks for the if bridge has low deth locked in it ( which is always false) so it goes to else block which is a revert statement. so the transaction reverts.
## Tools Used
Manual Review
## Recommended Mitigation Steps
Change the logic of assessdeth function to check for minimum deth locked in eth by changing to 
 if (IBridge(stethBridge).getDethValue() > C.ROUNDING_ZERO) 








## Assessed type

Invalid Validation
Insufficient check on dusty bid creation can DOS market system
Audit: 2024-03-dittoeth-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Summary

User can get around checks to create many miniscule bids and which DOS's the Order Book's matching system.

## Vulnerability Details

The protocol has a number of measures to try and reduce gas intensive matching between bids and asks. One such measure is a check in `_createBid()` to prevent the addition of dust bids onto the Order Book and avoid excessive looping in the matching algo.

As can be seen in the code snippet below, the user provided params `uint80 price` and `uint88 ercAmount` are multiplied and then compared against `LibAsset.minBidEth(asset)`.
If the product of `price` and `ercAmount` is below minimum bid, the function reverts.
However, user can get around this check to add dust amounts by setting a combination of the maximum possible price with the lowest possible ercAmount.

```
    function _createBid(
        address sender,
        address asset,
        uint80 price,
        uint88 ercAmount,
        bool isMarketOrder,
        MTypes.OrderHint[] memory orderHintArray,
        uint16[] memory shortHintArray
    ) private returns (uint88 ethFilled, uint88 ercAmountLeft) {
>>>     uint256 eth = ercAmount.mul(price);
>>>     if (eth < LibAsset.minBidEth(asset)) revert Errors.OrderUnderMinimumSize();

        // MORE  CODE
    }
```

It is somewhat restricted by the `uint` types of the input params so the largest `price` will be `1e24` and therefore lowest `ercAmount` will be `1e11` (equivalent to $0.35 today).

## POC

The test function below shows how the check can be gotten around to add 100 dust bids to the order book.
Add the function below to `BidOrders.t.sol` and run.

```solidity
    function test_FillOrderBookWithSmallBids() public {
        uint80 highPrice = 1e24;
        uint88 lowAmount = 100000000000; // 1e11 which is $0.00035 @ $3500 / ETH
        uint256 numOrders = 100;

        // deal Eth to user & deposit into protocol
        depositEth(receiver, 200 ether);
        assertEq(getBids().length, 0);
        for (uint256 i = 0; i < numOrders; i++) {
            fundLimitBidOpt(highPrice, lowAmount, receiver);
        }
        console.log("getBids().length: ", getBids().length);
        assertEq(getBids().length, numOrders);
        assertEq(getBids()[0].ercAmount, lowAmount);

    }
```

## Impact

A malicious user can add many hundreds of Dust bids to the Order Book at extremely high price.
This can completely DOS the Order Book and hence the Market System because any Asks that get created would need to loop through all of these high priced dust Bids before getting to any genuine bids; expending the Block Gas Limit before matching occurs.

The protocol does have a way to counter spam orders [see here](https://dittoeth.com/technical/ob#cancelling-spam-orders) but it is restrictive and not intended to do a clean up of this