xanoutas/solidity-security-findings huggingface dataset:
Depositing funds to non licensed vault from leads to stuck funds
Audit: 2024-04-dyad-findings
Severity: MEDIUM

## Lines of Code 
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L150
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L261

## Impact
All deposited Kerosine tokens inside Vault.kerosine.unbounded.sol get stuck and cannot be withdrawn

## Proof of Concept
Anyone can deposit no a non licensed vault since there is no check in VaultManagerV2.deposit() for the valid license. The user cannot withdraw them later because of an underflow error. getNonKeroseneValue(id) returns 0.
0 - value = revert 
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L150

## Reccomendation: 
Add a check if the vault is added and licensed in VaultManagerv2 before depositing the funds

## Assessed Type
DOS, loss of funds
Misimplementation of Withdraw Function
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.bounded.sol#L32-L42


# Vulnerability details

## Impact
The function `withdraw` is meant to retrieve funds from the contract. However, this function is not implemented correctly. The function simply reverts every transaction with a `NotWithdrawable` error, making it impossible for anyone to withdraw funds from the contract.



## Proof of Concept
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.bounded.sol#L32-L42

```solidity
function withdraw(
  uint    id,
  address to,
  uint    amount
) 
  external 
  view
    onlyVaultManager
{
  revert NotWithdrawable(id, to, amount); 
}

The `withdraw` function is intended to allow the Vault Manager to withdraw a certain amount of funds from the contract to a specified address. However, as it stands, this function simply triggers a revert, making withdrawal impossible. This could lead to a loss of funds for the contract users.


## Tools Used

## Recommended Mitigation Steps

Redesign the `withdraw` function to fulfill its purpose and allow withdrawals, while ensuring security measures are taken to prevent unauthorized or fraudulent withdrawals. Make sure the function:

1. Validates the caller: It should check that the caller is allowed to withdraw funds.
2. Validates the parameters: It should ensure that the amount requested for withdrawal does not exceed the contract's balance.
3. Implements the withdrawal: It should transfer the requested amount to the destination address.


## Assessed type

Error
Deposit into arbitrary Vaults in VaultmanagerV2
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L119


# Vulnerability details

## Vulnerability Details
The current implementation of the deposit function does not enforce a check to ensure that the vault has been previously added to the vaults mapping. This allows users to deposit assets into any vault address, including those that have not been vetted or approved, potentially leading to loss of funds.
## Impact
This issue poses a security risk as it could result in the deposit of user funds into unauthorized or malicious vaults, leading to asset loss and undermining the trust in the system.

## Proof of Concept
- The deposit function in `VaultManagerV2` does not check if the vault is in the vaults mapping: [`VaultManagerV2.sol#deposit`](https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L119)

## Tools Used
Manual Review

## Recommended Mitigation Steps
Update the deposit function to include a check that the vault address is contained within the vaults mapping.
VaultManagerV2.sol:
``` diff
 ...
function deposit(
    uint    id,
    address vault,
    uint    amount
  ) 
    external 
      isValidDNft(id)
  {
+   if (!vaults[id].contains(vault) || vaultsKerosene[id].contains(vault)) revert UnAthorizedVault;
    idToBlockOfLastDeposit[id] = block.number;
    Vault _vault = Vault(vault);
    _vault.asset().safeTransferFrom(msg.sender, address(vault), amount);
    _vault.deposit(id, amount);
  }


## Assessed type

Context
zero transfer transfer can revert.
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.unbounded.sol#L30


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
check amount before transferring the amount.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
  function withdraw(
    uint    id,
    address to,
    uint    amount
  ) 
    external 
      onlyVaultManager
  {
    id2asset[id] -= amount;
@>    asset.safeTransfer(to, amount); 
    emit Withdraw(id, to, amount);
  }

This is a bit concerning as some ERC20 implementations revert on zero value transfers (see https://github.com/d-xo/weird-erc20#revert-on-zero-value-transfers). 
## Tools Used

## Recommended Mitigation Steps
use amount>0;


## Assessed type

Token-Transfer
Missing checks wrt. min/max prices returned from the oracle
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/4a987e536576139793a1c04690336d06c93fca90/src/core/Vault.sol#L100


# Vulnerability details

## Impact

Upon large price swings, the system will continue operating on the stale prices, not on real prices.

## Proof of Concept

Function [Vault::assetPrice()](https://github.com/code-423n4/2024-04-dyad/blob/4a987e536576139793a1c04690336d06c93fca90/src/core/Vault.sol#L91-L103) is as follows:

```solidity
  function assetPrice() 
    public 
    view 
    returns (uint) {
      (
        ,
        int256 answer,
        , 
        uint256 updatedAt, 
      ) = oracle.latestRoundData();
      if (block.timestamp > updatedAt + STALE_DATA_TIMEOUT) revert StaleData();
      return answer.toUint256();
  }
```

Chainlink aggregators include circuit breakers which kick in upon large price swings. The oracle will then start returning `minAnswer` or respectively `maxAnswer`, when the price falls too much, or rises too much.

_Note: this is a well known issue, which has been raised multiple times already in Code4rena audits; see e.g. [here](https://github.com/code-423n4/2023-07-moonwell-findings/issues/340)._

## Tools Used

Manual audit

## Recommended Mitigation Steps

Consider [monitoring for min/max answers](https://docs.chain.link/data-feeds#check-the-latest-answer-against-reasonable-limits), and reverting if the returned oracle prices come too close to them.


## Assessed type

Oracle
Liquidators can maximize their liquidation rewards
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L205-L228


# Vulnerability details

## Impact
The current implementation allows a liquidator to manipulate the collaterization ratio of a liquidateable position to maximize their reward during liquidation. So a malicious actor could deposit additional collateral into a position to increase the calculated reward (but still below 150%), leading to a larger gain from the liquidation.

## Proof of Concept
1. **Liquidateable Position**:
   If a position's collaterization ratio falls below 150%, it becomes liquidateable. For example, a position with a ratio of 100%.
2. **Depositing for a Liquidateable Position**:
   The `VaultManagerV2.sol` contract allows anyone to deposit for a dNFT. A liquidator can increase the collaterization ratio of a liquidateable position by depositing additional collateral. For example, if the ratio is at 100%, a deposit could increase it to 145%.

3. **Triggering Liquidation with a High Reward**:
   If the liquidator has increased the collaterization ratio but kept it below 150%, they can trigger liquidation. The calculated reward becomes larger due to the additional collateral, resulting in an increased gain for the liquidator.

4. **Receiving a Larger Reward**:
   The reward for liquidation is based on the collaterization ratio and the collateral held by the vaults. By depositing additional collateral before triggering liquidation, the liquidator can manipulate the system to receive a larger reward, leading to potential abuse.

```solidity
  function liquidate(
    uint id,
    uint to
  ) 
    external 
      isValidDNft(id)
      isValidDNft(to)
    {
      uint cr = collatRatio(id);
      if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh();
      dyad.burn(id, msg.sender, dyad.mintedDyad(address(this), id));

      uint cappedCr               = cr < 1e18 ? 1e18 : cr;
      uint liquidationEquityShare = (cappedCr - 1e18).mulWadDown(LIQUIDATION_REWARD);
      uint liquidationAssetShare  = (liquidationEquityShare + 1e18).divWadDown(cappedCr);

      uint numberOfVaults = vaults[id].length();
      for (uint i = 0; i < numberOfVaults; i++) {
          Vault vault      = Vault(vaults[id].at(i));
          uint  collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare);
          vault.move(id, to, collateral);
      }
      emit Liquidate(id, msg.sender, to);
  }
```

## Tools Used
- Manual review

## Recommended Mitigation Steps
Implement restrictions on who can deposit into a dNFT to prevent unauthorized deposits that manipulate the collaterization ratio for exploitative purposes.



## Assessed type

Context
Integer Underflow Vulnerability in move()
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.sol#L47-L58


# Vulnerability details

## Impact
In the function `move`, an amount of an asset is subtracted from one id and added to another. The subtraction operation here doesn't contain a check to verify that the `from` id has enough assets to cover the `amount` being moved. This can lead to an underflow vulnerability. When subtraction operation attempts to make a variable less than zero, 'underflow' causes the number to wrap around and become incredibly large, near the maximum an uint256 variable can store (nearly 1.15e77). This will result in the user account having way over than the original expected balance.

## Proof of Concept
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.sol#L47-L58

```solidity
function move(
    uint from,
    uint to,
    uint amount
  )
    external
      onlyVaultManager
  {
    id2asset[from] -= amount;
    id2asset[to]   += amount;
    emit Move(from, to, amount);
  }
```


## Tools Used

## Recommended Mitigation Steps

The function `move` should perform a check before subtracting the `amount` to avoid underflow. This can be done using SafeMath library's sub() function, so the subtraction will revert automatically if the result is less than zero (i.e., an underflow occurs).

The code should look something like this:
```solidity
    id2asset[from] = id2asset[from].sub(amount); 
    id2asset[to]   = id2asset[to].add(amount);
```
The SafeMath library's sub function executes the subtraction operation and performs the necessary integer underflow check, which prevents the vulnerability.


## Assessed type

Under/Overflow
Uinitialized Local Variables.
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L279
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L260


# Vulnerability details

## Impact
The uninitialized variable `usdValue` in the `getTotalUsdValue` function can lead to incorrect calculations of the total USD value. If a vault is not licensed, the `usdValue` variable is never assigned a value, and its default value (0) is used in the calculation. This can lead to an underestimation of the total USD value, especially if there are multiple unlicensed vaults. This could have significant implications for financial reporting, investment decisions, and any logic that relies on the accuracy of the total USD value.

## Proof of Concept



Consider a scenario where there are three vaults, two of which are licensed and one is not. The licensed vaults have USD values of 1000 and 2000, respectively, while the unlicensed vault has a USD value of 500. The expected total USD value is 3500. However, due to the uninitialized `usdValue` for the unlicensed vault, the actual total USD value calculated by the function will be 3000 (1000 + 2000 + 0), leading to an incorrect result.

## Tools Used
Slither was used to help identify this vulnerability.

## Recommended Mitigation Steps
To mitigate this issue, ensure that the `usdValue` variable is always initialized before it is used in the calculation. One way to do this is by initializing `usdValue` to 0 at the beginning of the loop. This ensures that even if a vault is not licensed, its USD value contributes 0 to the total, rather than an uninitialized value.
```solidity
function getTotalUsdValue(
    uint id
) 
    public 
    view
    returns (uint) {
      uint totalUsdValue = 0; // Initialize totalUsdValue to 0
      uint numberOfVaults = vaults[id].length(); 
      for (uint i = 0; i < numberOfVaults; i++) {
        Vault vault = Vault(vaults[id].at(i));
        uint usdValue = 0; // Initialize usdValue to 0
        if (vaultLicenser.isLicensed(address(vault))) {
          usdValue = vault.getUsdValue(id);        
        }
        totalUsdValue += usdValue;
      }
      return totalUsdValue;
}
```
By initializing `usdValue` to 0, the function now correctly calculates the total USD value, even when some vaults are not licensed. This ensures that the function's output is accurate and reliable, mitigating the risk associated with the uninitialized variable.





## Assessed type

Other
Users cannot associate their DNFT with a Kerosene Vault
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L80-L91


# Vulnerability details

## ISSUE

When users want to associate their DNFT with a vault, they will either call `VaultManager.add()` to add to normal vault or [`VaultManager.addKerosene()`](https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L80-L91) to add to a kerosene vault. On calling the `addKerosene()`, the function checks if the particular vault is licensed.

```solidity
    // VaultManager.addKerosene
    function addKerosene(uint256 id, address vault) external isDNftOwner(id) {
        if (vaultsKerosene[id].length() >= MAX_VAULTS_KEROSENE) revert TooManyVaults();
        if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed();  // <--- Check is done here
        if (!vaultsKerosene[id].add(vault)) revert VaultAlreadyAdded();
        emit Added(id, vault);
    }
```

Normally, this check is supposed to be done by the `Licenser` contract (through the multisig) but in this function the check is carried out by the [`KeroseneManager`](https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/KerosineManager.sol#L44-L51) contract.

```solidity
    // ...
        if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed();
    // ...
```

The `KeroseneManager` is used to add/remove the vaults of exogenous collateral tokens (WETH/WSTETH) when calculating the asset price of Kerosene. In the [deployment script](https://github.com/code-423n4/2024-04-dyad/blob/main/script/deploy/Deploy.V2.s.sol), the Kerosene Manager adds the two "normal" vaults while the `Licenser` licenses the unbounded kerosene vault.

```solidity
    // ...
        kerosineManager.add(address(ethVault));
        kerosineManager.add(address(wstEth));
    // ...
        vaultLicenser.add(address(unboundedKerosineVault));
    // ...
```

The issue is that when a user wants to associate their DNFT with a kerosene vault (unbounded type in this case), the Vault Manager contract checks and calls `KeroseneManager.isLicensed()` and the contract cannot find the unbounded kerosene vault in the set. This causes a revert in the `addKerosene()` call and users cannot associate a DNFT with the kerosene vault.

## POC

```solidity
// SPDX-License-Identifier: MIT
pragma solidity =0.8.17;

import "forge-std/console.sol";
import {StdUtils} from "lib/forge-std/src/StdUtils.sol";
import "forge-std/Test.sol";
import {IERC20} from "lib/forge-std/src/interfaces/IERC20.sol";

import {DeployV2, Contracts} from "../../script/deploy/Deploy.V2.s.sol";
import {Licenser} from "../../src/core/Licenser.sol";
import {Parameters} from "../../src/params/Parameters.sol";
import {IDNft} from "src/interfaces/IDNft.sol";

contract V2Test is StdUtils, Test, Parameters {
    Contracts contracts;

    IDNft dnft = IDNft(MAINNET_DNFT);

    address payable weth =
Small positions don't offer enough incentives for liquidators
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L205-L228


# Vulnerability details

## Impact
Positions that are small in size, don't offer enough rewards to the liquidator and hence will never be liquidated, accruing debt on the protocol.

## Proof of Concept
Liquidators are paid 20% as rewards but this might not be enough in the combination  of two conditions:
1. Small Position
2. Generall high gas fees on mainnet

Ex:

Alice Puts *150 Dollars* of ETH and borrows *100 Dollars* of DYAD, now the price of ETH drops such that CR = 140%.

The [liquidator earns 77%](https://docs.google.com/spreadsheets/d/1VBOJnaUcnwlIc-nShB0-eBRlDg6ceChcDf6O4EgHCuU/edit#gid=0) of the value of the Note.
Now for a position of *150 Dollars*, this will be $115!
The liquidator will burn *100 Dollars* of DYAD, and get a profit of *15 Dollars*.

$15 can many times be less than the fees + operating cost of the liquidator



## Tools Used
Manual 

## Recommended Mitigation Steps

Restrict users having small collateral positions.


## Assessed type

MEV
getKeroseneValue method will always return 0
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L280


# Vulnerability details

## Impact
This will lead to users not being able to mint new Dyad, despite the fact that they already deposited Kerosine in Kerosine vault. This is because KerosineManager will hold only (wETH, wstETH) as per DeployV2.s.sol (https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/script/deploy/Deploy.V2.s.sol#L64) and as per documentation.

## Tools Used
Manual review

## Recommended Mitigation Steps
change keroseneManager.isLicensed with vaultLicenser
```solidity
function getKeroseneValue(uint id) public view returns (uint) {
        uint totalUsdValue;
        uint numberOfVaults = vaultsKerosene[id].length();
        for (uint i = 0; i < numberOfVaults; i++) {
            Vault vault = Vault(vaultsKerosene[id].at(i));
            uint usdValue;
            //
            // @audit: BUG! have to be changed, because keroseneValue will be 0, because KeroseneManager cannot hold kerosineVaults
            if (vaultLicenser.isLicensed(address(vault))) {
                usdValue = vault.getUsdValue(id);
            }
            totalUsdValue += usdValue;
        }
        return totalUsdValue;
    }
```


## Assessed type

Context
Liquidators are not incentivized to liquidate small positions  
Audit: 2024-04-dyad-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L119-L131
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L156-L181
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L184-L202


# Vulnerability details

## Impact
The protocol allows to create vaults and provide collateral for minting `DYAD` with no lower limit. As such, multiple low value positions can exist. However, there is no incentive to liquidate low value positions because of gas cost. This can lead to the `DYAD` undercollaterization.


## Proof of Concept
Liquidators liquidate users for the profit they can make. If there is no profit to be made then there will be no one to call the `liquidate` function. For example a position could exist with a relatively low collateral value. This user is undercollateralized and must be liquidated in order to ensure that the protocol remains overcollateralized. If a liquidator wishes to liquidate this user, they will first need to mint some `DYAD` which involves gas cost. Because the value of the collateral is low, after gas costs, liquidators will not make a profit liquidating this user. In the end these low value positions will never get liquidated, leaving the protocol with bad debt and can even cause the protocol to be undercollateralized with enough small value accounts being underwater.


## Tools Used
Manual review


## Recommended Mitigation Steps
A potential fix would be to set a minimum threshold for collateral value which has to be exceeded in order for a user to mint `DYAD`.


## Assessed type

Other