xanoutas/solidity-security-findings huggingface dataset:
An asset's depositLimit can be set as < TotalAssetDeposits
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L94-L104
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L56-L58


# Vulnerability details

## Summary
The method `updateAssetDepositLimit()` doesn't check the **TotalAssetDeposits**. It can be set as < **TotalAssetDeposits**.

## Vulnerability Details
The method `updateAssetDepositLimit()` sets the input amount as **depositLimit** for an asset. It doesn't check the total assets Deposited. If set incorrectly can cause `depositAsset()` and `getAssetCurrentLimit()` to revert.

### POC Test
Add the following test in LRTDepositPoolTest.t.sol
```
function test_GetAssetCurrentLimitUnderflows() external {
        vm.startPrank(manager);
        lrtConfig.updateAssetDepositLimit(address(stETH), 10 ether);
        vm.stopPrank();

        // deposit 1 ether stETH
        vm.startPrank(alice);
        stETH.approve(address(lrtDepositPool), 6 ether);
        lrtDepositPool.depositAsset(address(stETH), 6 ether);
        vm.stopPrank();

        vm.startPrank(manager);
        lrtConfig.updateAssetDepositLimit(address(stETH), 5 ether);
        vm.stopPrank();
        
        // vm.expectRevert();
        lrtDepositPool.getAssetCurrentLimit(address(stETH));
    }
```

### POC Logs
```
Encountered 1 failing test in test/LRTDepositPoolTest.t.sol:LRTDepositPoolGetAssetCurrentLimit
[FAIL. Reason: Arithmetic over/underflow] test_GetAssetCurrentLimitUnderflows() (gas: 217600)
```

## Impact
If the **depositLimit** is set as < **TotalAssetDeposits** then `getAssetCurrentLimit()` will underflow. Which will cause `depositAsset()` to revert.

## Recommendations
Ensure that **depositLimit** >= **TotalAssetDeposits** while setting in `updateAssetDepositLimit()`


## Assessed type

Under/Overflow
Users may not get rseth due to exchange rate manipulation
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L52-L76
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L109
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L141


# Vulnerability details

## Impact
In deposit() function _mintRsETH() uses following formula to calculate how much rseth should be minted
```solidity
rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
```
The problem is that an attacker can manipulate price of rseth and other users when depositng may get 0 rseth.

## Proof of Concept
By minting a small amount of rseth and then transferring a large amount of another asset, the attacker can significantly distort the calculated rseth price. Subsequently, when other users attempt to deposit their assets, they won't receive the correct amount of rseth due to the inflated price.

getTotalAssetDeposits() uses balanceOf() to fetch balances of the contracts
```solidity
function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply();

        if (rsEthSupply == 0) {
            return 1 ether;
        }

        uint256 totalETHInPool;
        address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);

        address[] memory supportedAssets = lrtConfig.getSupportedAssetList();
        uint256 supportedAssetCount = supportedAssets.length;

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);

            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;

            unchecked {
                ++asset_idx;
            }
        }

        return totalETHInPool / rsEthSupply;
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider not using balanceOf for price calculation


## Assessed type

Oracle
Deprecated method latestAnswer() is used in ChainlinkPriceOracle contract
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/oracles/ChainlinkPriceOracle.sol#L37-L39


# Vulnerability details

## Summary
The Chainlink oracle is used to get the token price from the price feed. But the method `latestAnswer()` has been deprecated as per Chainlink docs.

## Vulnerability Details
The method `latestAnswer()` has been deprecated by Chainlink. `latestRoundData()` should be used as per their documentation.

https://docs.chain.link/data-feeds/api-reference#latestanswer

## Recommendations 
Implement the **AggregatorV3Interface** and use `latestRoundData()` instead of deprecated `latestAnswer()`


## Assessed type

Oracle
Users cannot redeem rsETH tokens for LST assets
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/RSETH.sol#L54


# Vulnerability details

The `Kelp DAO` protocol lacks implementing a function/feature for redeeming the `rsETH` tokens for underlying LST assets. Subsequently, users can stake their LST assets to the protocol but cannot retrieve their LST assets.

## Proof of Concept

Users can stake LST assets (i.e., `stETH`, `cbETH`, and `rETH` tokens) to the `Kelp DAO` protocol via the [`LRTDepositPool::depositAsset()`](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L119) and receive minted `rsETH` tokens in exchange. The LRT Manager will then deposit the LST assets into `EigenLayer`'s `Strategy` contracts through the [NodeDelegator::depositAssetIntoStrategy()](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/NodeDelegator.sol#L51).

The `RSETH` contract provides the [`burnFrom()`](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/RSETH.sol#L54) callable by the protocol's authorized burner. However, the protocol does not implement a function/feature for redeeming the `rsETH` tokens for underlying LST assets for users.

## Impact

Users cannot retrieve the underlying LST assets. Moreover, the minted `rsETH` tokens may not be accepted by the DeFi community because they cannot be redeemed for their backed LST assets.

These harmful consequences can impact both the `Kelp DAO` protocol and its users.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Implement a function/feature for redeeming the `rsETH` tokens for underlying LST assets.


## Assessed type

Other
If the protocol supports more LST assets in the future with different decimal precision than 1e18 getRSETHPrice will return heavily skewed results
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L45-L79


# Vulnerability details

## Impact
If the protocol supports more LST assets in the future with different decimal precision than 1e18 `getRSETHPrice` will return heavily skewed results

## Proof of Concept
Since `LRTConfig::addNewSupportedAsset` exists, it is reasonable to assume new supported LST assets will be added in the future.
```javascript
    /// @dev Adds a new supported asset
    /// @param asset Asset address
    /// @param depositLimit Deposit limit for the asset
    function addNewSupportedAsset(address asset, uint256 depositLimit) external onlyRole(LRTConstants.MANAGER) {
        _addNewSupportedAsset(asset, depositLimit);
    }
```

The problem arises from the fact that the [function](https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L52-L79) `LRTOracle::getRSETHPrice` does not account for the decimals of the supported asset. Currently, it assumes that all LST tokens will be in 1e18 decimals as there is no conversion for the less decimal or more decimal tokens into one standard way to calculate the totalEthInPool, if one of the LST has less than 18 decimals that calculations of totalEthInPool will be underestimated by a margin, depends how many decimals the newly added LST have. 

```javascript
        uint256 totalETHInPool;
        address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);

        address[] memory supportedAssets = lrtConfig.getSupportedAssetList();
        uint256 supportedAssetCount = supportedAssets.length;

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);
        
            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;

            unchecked {
                ++asset_idx;
            }
        }

        return totalETHInPool / rsEthSupply;
    }
```

LST assets with less than 18 decimals like [STSOL](https://www.coingecko.com/en/coins/lido-staked-sol)exist and if such an LST is added, the results will be heavily skewed, thus minting significantly less/more than the function should.

Issue Example:
1. `getRSETHPrice` loops through the supported assets, multiplying `totalAssetAmt * assetER` and adding them all to `totalETHInPool`
2. Let's take an example of 3 supported assets: `stETH` (1e18), `rETH` (1e18), `STSOL` (1e9)
3. Users have deposited 1e18 `stETH`, and 1e18 `rETH` and for ease as of now their exchange rate is equal to eth 1:1
4. Now Another token was added whose decimals are 9 which is also 1:1 with ETH and 1 token for it is deposited which will be equal to 1e9
5. Now we expect the totalEthInPool will be returned as 3e36, where later it will be divided by rsEth total
First depositor can steal all users funds
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L119-L144
https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L109
https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L52-L79


# Vulnerability details

## Impact

First depositor can deposit a single wei of asset into `LRTDepositPool` then donate a larger amount of asset funds to greatly inflate the RSETH price. Due to truncation when calculating the amount or RSETH to be minted in `getRsETHAmountToMint`, all later depositors will receive 0 RSETH for their deposits.

## Proof of concept

The issue starts with the depositAsset function which allow any user to deposit any amount of supported asset into the pool, to get the amount of RSETH that should be minted to the depositor the `getRsETHAmountToMint` function is called, it uses the following formula:

```solidity
rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
```

To calculate the RSETH price the function `lrtOracle.getRSETHPrice()` uses the following formula:

`sum(totalAssetDeposited * assetETHPrice) / rsethTotalSupply`

Basically calculating the value of all supported assets deposited in the pool denominated in ETH and dividing by the RSETH total supply.

Here is where the first depositor can manipulate the RSETH price, by initially depositing 1 wei (a very small amount) of a given supported asset when RSETH total supply is zero, this will result in the following states :

`rsethTotalSupply = 1`

`totalAssetDeposited = 1`

`getRSETHPrice() = 1e18`

And just after that the attacker makes a donation to the pool contract by transferring a larger asset amount directly to the pool (with ERC20 transferFrom), if we suppose the attacker sends 10 RETH we will get the following states now:

`rsethTotalSupply = 1`

`totalAssetDeposited = 10e18 + 1`

`getRSETHPrice() = 10e18`

Thus the attacker managed to massively inflate the RSETH price and now all later depositor will receive 0 RSETH when depositing assets to the pool as the truncation in `getRsETHAmountToMint` will result in 0 RSETH getting minted.

- POC:

Add the below lines at the end of `LRTDepositPoolTest.t.sol`.

To run directly use the command: `forge test --mc FirstDepositorAttack`

```solidity
import {LRTConfig, ILRTConfig} from "src/LRTConfig.sol";
import {IRSETH} from "../src/interfaces/IRSETH.sol";

contract RealisticLRTOracleMock {
    LRTConfig public lrtConfig;
    ILRTDepositPool public lrtDepositPool;

    constructor(address _lrtConfig, address lrtDepositPoolAddress) {
        lrtDepositPool = ILRTDepositPool(lrtDepositPoolAddress);
        lrtConfig = LRTConfig(_lrtConfig);
    }

    function getAssetPrice(address) public pure returns (uint256) {
        return 1e18;
    }

    //@audit using same logic used in the actual LRTOracle (not just retu
Wrong RSETH amount minted because `getRSETHPrice()` includes current depositor amount
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L52-L79
https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L95-L110
https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L119-L144


# Vulnerability details

## Impact

The `getRSETHPrice()` function is used in `getRsETHAmountToMint` to determine the amount of RSETH that should be minted to the depositor, but as `getRSETHPrice()` includes the current depositor amount into the total asset deposited calculation the RSETH price returned will be higher than expected and thus the depositor will receive less RSETH amount that what was intended.

## Proof of concept

The issue occurs in the `getRSETHPrice()` below:

```solidity
function getRSETHPrice() external view returns (uint256 rsETHPrice) {
    address rsETHTokenAddress = lrtConfig.rsETH();
    uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply();

    if (rsEthSupply == 0) {
        return 1 ether;
    }

    uint256 totalETHInPool;
    address lrtDepositPoolAddr = lrtConfig.getContract(
        LRTConstants.LRT_DEPOSIT_POOL
    );

    address[] memory supportedAssets = lrtConfig.getSupportedAssetList();
    uint256 supportedAssetCount = supportedAssets.length;
    //@audit will include the current user deposit in the asset ==> expectedShares=1e18
    for (uint16 asset_idx; asset_idx < supportedAssetCount; ) {
        address asset = supportedAssets[asset_idx];
        uint256 assetER = getAssetPrice(asset);

        uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr)
            .getTotalAssetDeposits(asset);
        totalETHInPool += totalAssetAmt * assetER;

        unchecked {
            ++asset_idx;
        }
    }

    return totalETHInPool / rsEthSupply;
}
```

as it can be seen the function calculates the RSETH price with the following formula:

`RSETHPrice = (sum(totalAssetDeposited * assetETHPrice) = totalETHInPool) / rsEthSupply`

To get the total asset deposited in the pool the `LRTDepositPool.getTotalAssetDeposits` function is called which under the hood calls `LRTDepositPool.getAssetDistributionData` :

```solidity
function getAssetDistributionData(
    address asset
)
    public
    view
    override
    onlySupportedAsset(asset)
    returns (
        uint256 assetLyingInDepositPool,
        uint256 assetLyingInNDCs,
        uint256 assetStakedInEigenLayer
    )
{
    // Question: is here the right place to have this? Could it be in LRTConfig?
    assetLyingInDepositPool = IERC20(asset).balanceOf(address(this));

    uint256 ndcsCount = nodeDelegatorQueue.length;
    for (uint256 i; i < ndcsCount; ) {
        assetLyingInNDCs += IERC20(asset).balanceOf(nodeDelegatorQueue[i]);
        assetStakedInEigenLayer += INodeDelegator(nodeDelegatorQueue[i])
            .getAssetBalance(asset);
        unchecked {
            ++i;
stETH/ETH, cbETH/ETH, rETH/ETH chainlink oracle has too long of heartbeat and deviation threshold which can wrong calculations and loss of funds
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L45-L79


# Vulnerability details

## Impact
stETH/ETH, cbETH/ETH, rETH/ETH chainlink oracle has too long of heartbeat and deviation threshold which can wrong calculations and loss of funds
## Proof of Concept
Kelp is using the chainlink oracle to get the exchange rate for three LST tokens which is evident as there are no two step conversions in the chainlink oracle:

- stETH
- cbEtH
- rETH

But if we look at the chainlink price feeds all of them have the heartbeat of 24 hours and deviation threshold of 2% for cbEth, 1% for rEth, and 0.5% for stETH which in our case is actually too much and will lead to wrong calculations and mintings due to stale price/exchange rate and will not be updated unless price deviation is 2% or more :

## [stETH Price Feed](https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth)
![image](https://user-images.githubusercontent.com/68193826/283187551-966fc13a-a4e7-4b8e-9ce8-cfe0d1bd1fd3.png)


## [cbETH Price Feed](https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth)
![image](https://user-images.githubusercontent.com/68193826/283188130-b7bf5b98-8892-4fa3-9483-f4012b529705.png)


## [rETH Price Feed](https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth)
![image](https://user-images.githubusercontent.com/68193826/283188333-61f8f459-7da7-41fd-a57a-38f71f3435d7.png)

Now imagine the scenario for the sake of example for one asset cbETH for which the deviation threshold is 2% and heartbeat is 24 hours and within that 24 hours deviation of 1.8% occurs in positive and someone tries to deposit via the kelp.

1. Alice has 4 cbETH which at the current price of cbEth against ETH (1.0538) are worth $8460
2. But the price fetched from the Chainlink oracle is off by 1.8% which means the price returned will actually be 1.8% less than the actual price.
3. Alice will be minted fewer tokens than she is accounted for and the total loss for her will account to be 1.8% of $8460 which is equal to $152 which is a substantial amount of loss and will increase as the deposited amount increases.

## Tools Used
Chainlink Price Feed Data and some common sense plus validation from solodit.
## Recommended Mitigation Steps
There are two options:

1. Use custom-configured TWAP oracles instead.
2. Use chainlink ETH/USD pair which has the 1-hour heartbeat and 0.5% deviation along with stETH/USD price feed which also has 1 1-hour heat beat and 1% deviation to do two-step conversions and prevent stale prices. The only problem is I was not able to find cbETH/USD and rETH/USD pairs on the chainlink price feed.


## Assessed type

Oracle
Decimal Precision Vulnerability in the calculations of ``totalETHInPool``
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L71


# Vulnerability details

## Description
The ``LRTOracle.sol`` contract exposes a high-level vulnerability on line 71 within the ``getRSETHPrice`` function. This vulnerability, if exploited, has the potential to cause substantial financial damage to all users interacting with functions relying on this oracle. The issue arises from an inadequate adjustment for 18 decimals during the multiplication of ``totalAssetAmt`` and ``assetER``, resulting in an incorrect computation with 36+ decimals, which can adversely impact various high functionalities.

## Impact
This vulnerability extends beyond the ``getRSETHPrice`` function and affects functions throughout the system. For instance, the ``getRsETHAmountToMint`` function within the ``LRTDepositPool.sol`` contract utilizes the flawed oracle calculation. Consequently, this flaw cascades into the ``_mintRsETH`` function, which is subsequently invoked by users through the ``depositAsset`` function. Users deposit collateral, receive an amount of RsETH, and any miscalculation in the oracle significantly impacts the resulting RsETH amount, jeopardizing user funds.

## POC
1) Admin adds multiple supportedAssets
2) Attacker first mints some RSETH making the supply of RsETH > 0
3) Bob mints other RsETH,raising the totalAssetAmt doing so
5) Alice wants to mint but the price is highly wrong due to large amount of ``totalETHInPool`` and the division will result in more decimals than it should be

## Tools Used
manual review

## Recommended Mitigation Steps
```
totalETHInPool += (totalAssetAmt * assetER) / 1e18;
```


## Assessed type

Decimal
The calculation of rsETH minting amount in `getRsETHAmountToMint()` function is not scaled
Audit: 2023-11-kelp-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L95-L110


# Vulnerability details

## Impact

During `depositAsset()` the amount of `rsETH` to mint is calculated by `getRsETHAmountToMint()` function which doesn't scale the vale of `Asset/ETH exchange rate`.

The inaccurate calculation in the `getRsETHAmountToMint()` function can result in an incorrect amount of `rsETH` being minted when users deposit assets if the `deposit asset` is not with 18 decimals. This discrepancy can lead to financial losses for users because they will receive slightly lower `rsETH minted amount`.

## Explanation

The vulnerability is related to the `getRsETHAmountToMint()` function in the `LRTDepositPool` contract. This function calculates the amount of rsETH to mint when a user deposits a specific `asset` with a given `amount`. However, the calculation does not scale the `asset` based on its decimal places, which can result in inaccurate conversions.

```js
    /// @notice View amount of rsETH to mint for given asset amount
    /// @return rsethAmountToMint Amount of rseth to mint
    function getRsETHAmountToMint(
        address asset,
        uint256 amount
    ) public view override returns (uint256 rsethAmountToMint) {
        // setup oracle contract
        address lrtOracleAddress = lrtConfig.getContract(
            LRTConstants.LRT_ORACLE
        );
        ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress);

        // calculate rseth amount to mint based on asset amount and asset exchange rate
        rsethAmountToMint =
            (amount * lrtOracle.getAssetPrice(asset)) /
            lrtOracle.getRSETHPrice();
    }
```

## Proof of Concept

- `getAssetPrice()` return the exchange rate for `USDC/rsETH` which value will be with the `asset decimal places`.

    ```js
        /// @notice Provides Asset/ETH exchange rate
        /// @dev reads from priceFetcher interface which may fetch price from any supported oracle
        /// @param asset the asset for which exchange rate is required
        /// @return assetPrice exchange rate of asset
        function getAssetPrice(
            address asset
        ) public view onlySupportedAsset(asset) returns (uint256) {
            return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset);
        }
    ```

- `rsETH` has `18 decimal places`.

So, assume the example that the `deposited asset` is `USDC` which has `6 decimal places`. So due to the fact that `USDC` is with `6 decimals` the return minted amount of `getRsETHAmountToMint()` will be slightly lower than is actually should be.

## Tools Used

Manual Code Inspection

## Recommended Mitigation Steps

Scale the calculation of rsETH minting amount.

```diff
    /// @notice View amount of rsETH to mint for given asset amount
    /// @return rsethAmountToMint Amount of rseth to mint
    function getRsETHAmountToMint(
        address asset,
        uint256 amount
    ) pub