xanoutas/solidity-security-findings huggingface dataset:
Unchecked Low-Level Call in _depositRawEthIntoLido Function
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L34-L41


# Vulnerability details

## Impact

The `_depositRawEthIntoLido` function does not check the return value of the `call` function. If the ETH transfer to the Lido contract fails, the function will continue execution without detecting the failure. This could result in incorrect accounting of deposited funds, loss of ETH, and inconsistent contract state.


## Proof of Concept

The vulnerable code snippet:
 
Function [_depositRawEthIntoLido](https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L34-L41)

```solidity
    function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
        // check before-after balances for 1-wei corner case
        uint256 _balBefore = stEth.balanceOf(address(this));
        // TODO call submit() with a referral?
        payable(address(stEth)).call{value: _initialETH}("");
        uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
        return _deposit;
    }
```



## Tools Used

in-house tool, manual analysis

## Recommended Mitigation Steps

Check return value of `call` function


## Assessed type

call/delegatecall
Unchecked .call() Low-Level Function Result
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L38


# Vulnerability details

## Details
In ZapRouterBase contract, ```_depositRawEthIntoLido``` function use low-level .call() function in order to send native ether to Lido contract and, it doesn't check the success of the .call() function.  

## Possibilities
- It can be reverted in Lido but execution will continue
1) Lido is a pausable contract it doesn't accept staking while paused (see: https://github.com/lidofinance/lido-dao/blob/5fcedc6e9a9f3ec154e69cff47c2b9e25503a78a/contracts/0.4.24/Lido.sol#L928)
2) Lido has daily staking limit implementation. Highly volatile days, it can be reverted and this limit is not applied by user based, it's applied contract based. (see: https://docs.lido.fi/guides/lido-tokens-integration-guide#staking-rate-limits)
- Temporary network issues, .call() can be failed while whole execution is completed

## Impact
ETH is locked in EbtcLeverageZapRouter while adding margin via native ether or wrapped ether

It doesn't affect ```openCdp``` function because opening position without margin is impossible but it affect ```adjustCdp``` function while adding margin.

## Proof of Concept
In the scenario, we will add revert line to fallback function to CollateralTokenTester contract in order to simulate pausing or reaching staking limit and, we expect our adjust function should not be executed
```solidity
    receive() external payable {
        revert("Staking Limit Reached");
        deposit();
    }
```

This function used for initial CDP, it creates approximately 3x position:
```solidity
    function createLeveragedPosition(MarginType marginType) private returns (address user, bytes32 expectedCdpId) {
        user = vm.addr(userPrivateKey);

        uint256 _debt = 1e18;
        uint256 flAmount = _debtToCollateral(_debt);
        uint256 marginAmount = flAmount / 2;

        if (marginType == MarginType.stETH) {
            _dealCollateralAndPrepForUse(user);
            vm.prank(user);
            collateral.approve(address(leverageZapRouter), type(uint256).max);
        } else if (marginType == MarginType.wstETH) {
            _dealCollateralAndPrepForUse(user);
            vm.startPrank(user);
            collateral.approve(address(testWstEth), type(uint256).max);
            IWstETH(testWstEth).wrap(collateral.balanceOf(user));
            IERC20(testWstEth).approve(address(leverageZapRouter), type(uint256).max);
            marginAmount = IWstETH(testWstEth).getWstETHByStETH(marginAmount);
            vm.stopPrank();
        } else if (marginType == MarginType.ETH) {
            vm.deal(user, type(uint96).max);
        } else if (marginType == MarginType.WETH) {
            vm.deal(user, type(uint96).max);
            vm.startPrank(user);
            IWrappedETH(testWeth).deposit{value: marginAmount}();
            IERC20(testWeth).approve(address(leverageZapRouter), typ
Reverting all actions
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-protocol/packages/contracts/contracts/LeverageMacroBase.sol#L241


# Vulnerability details

## Impact
If willSweep variable is set True, when a user do an action like open CDP, at line 241, sweepToCaller() function is called.
```
    function sweepToCaller() public {
        _assertOwner();
        /**
         * SWEEP TO CALLER *
         */
        // Safe unchecked because known tokens
        uint256 ebtcBal = ebtcToken.balanceOf(address(this));
        uint256 collateralBal = stETH.sharesOf(address(this));


        if (ebtcBal > 0) {
            ebtcToken.transfer(msg.sender, ebtcBal);
        }


        if (collateralBal > 0) {
            stETH.transferShares(msg.sender, collateralBal);
        }
    }
```
At first calls _assertOwner() function.
```
    function _assertOwner() internal {
        // Reference will compare to variable,
        require(owner() == msg.sender, "Must be owner");
    }
```
It checks that if msg.sender is not owner, transaction will revert. So all action like open CDP and close CDP will revert.
## Proof of Concept


## Tools Used
manual
## Recommended Mitigation Steps
using _sweepEbtc() and _sweepStEth() functions instead of sweepToCaller() function.


## Assessed type

Error
ETH Lockup Due to Lack of Error Handling in EbtcLeverageZapRouter Contract
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router%2Fsrc%2FZapRouterBase.sol#L34-L41


# Vulnerability details

When converting  raw `ETH` to `stETH` the `_depositRawEthIntoLido` function sends ETH to the Lido contract using a low-level call without checking for success or handling potential failures.
 If the conversion fails, the contract does not revert the transaction or provide feedback to the user. This can lead to the user's ETH being locked in the contract without being converted to stETH.

Reasons for Conversion Failure:
1. **Stake Limit Reached**: If Lido’s staking limit is set and the limit is reached, the deposit will fail.
2. **Staking Paused**: If staking is paused in the Lido contract, the deposit will fail.
3. **Insufficient Gas**: If there isn’t enough gas to complete the transaction, the deposit will fail.

## Impact
User's ETH sent with the transaction will get locked in the `EbtcLeverageZapRouter` contract if the conversion fails.


## Proof of Concept

https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router%2Fsrc%2FZapRouterBase.sol#L34-L41
 `_depositRawEthIntoLido` function that does not handle potential errors:

```solidity
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    uint256 _balBefore = stEth.balanceOf(address(this));
    payable(address(stEth)).call{value: _initialETH}("");
    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    return _deposit;
}
```


## Tools Used
Manual Review 

## Recommended Mitigation Steps
 Use the `success` variable to check if the call to the Lido contract succeeded.
 Verify that the stETH balance increased after the call.
 Provide clear and descriptive error messages when the transaction fails.

An example of the fix can be:

```solidity
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    uint256 _balBefore = stEth.balanceOf(address(this));

    (bool success, ) = payable(address(stEth)).call{value: _initialETH}("");
    require(success, "EbtcZapRouter: ETH to stETH conversion failed");

    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    require(_deposit > 0, "EbtcZapRouter: No stETH received");
    
    return _deposit;
}
```


## Assessed type

Error
Failure in ETH to stETH Conversion Without User Feedback in EbtcLeverageZapRouter Contract
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router%2Fsrc%2FZapRouterBase.sol#L34-L41


# Vulnerability details

The `EbtcLeverageZapRouter` contract converts ETH to stETH using the Lido protocol. However, the current implementation of the `_depositRawEthIntoLido` function does not handle errors that may occur during the conversion process. Specifically, the function uses a low-level `call` to the Lido contract without checking the return value, which means that if the deposit fails for any reason, the user will not be notified. 
This lack of feedback can result in user confusion.

Conversation can fail if any of the following occurs:
1. **Stake Limit Reached**: If Lido’s staking limit is set and the limit is reached, the deposit will fail.
2. **Staking Paused**: If staking is paused in the Lido contract, the deposit will fail.
3. **Insufficient Gas**: If there isn’t enough gas to complete the transaction, the deposit will fail.


## Impact
 Users will not receive any feedback if the deposit to Lido fails, leading to confusion as there's no appropriate error message 

## Proof of Concept
 `_depositRawEthIntoLido` function that does not handle the potential errors
https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router%2Fsrc%2FZapRouterBase.sol#L34-L41
```solidity
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    uint256 _balBefore = stEth.balanceOf(address(this));
    payable(address(stEth)).call{value: _initialETH}("");
    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    return _deposit;
}
```

## Tools Used
Manual Review 

## Recommended Mitigation Steps
Use the `success` variable to check if the call to the Lido contract succeeded.
Provide clear and descriptive error messages when the transaction fails.

```solidity
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    uint256 _balBefore = stEth.balanceOf(address(this));

    (bool success, ) = payable(address(stEth)).call{value: _initialETH}("");
    require(success, "EbtcZapRouter: ETH to stETH conversion failed");

    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    
    return _deposit;
}
```


## Assessed type

Context
Incorrect Comparison Logic in Post-Operation Checks
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-protocol/packages/contracts/contracts/LeverageMacroBase.sol#L277-L289


# Vulnerability details


## Impact
The `_doCheckValueType` function, which is crucial for post-operation validation, contains reversed comparison logic for the `gte` (greater than or equal to) and `lte` (less than or equal to) operators. This reversal causes the function to perform the opposite checks than intended, potentially allowing operations to pass validation when they should fail, or fail when they should pass.

This issue could lead to:
1. Incorrect validation of CDP (Collateralized Debt Position) states after operations.
2. Potential manipulation of the system by exploiting these misaligned checks.


## Proof of Concept
The issue is located in the `_doCheckValueType` function of the LeverageMacroBase contract:

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-protocol/packages/contracts/contracts/LeverageMacroBase.sol#L277-L289

Specifically, the problematic comparisons are:

```solidity
} else if (check.operator == Operator.gte) {
    require(check.value >= valueToCheck, "!LeverageMacroReference: gte post check");
} else if (check.operator == Operator.lte) {
    require(check.value <= valueToCheck, "!LeverageMacroReference: let post check");
}
```

These comparisons are reversed and should be:

```solidity
} else if (check.operator == Operator.gte) {
    require(valueToCheck >= check.value, "!LeverageMacroReference: gte post check");
} else if (check.operator == Operator.lte) {
    require(valueToCheck <= check.value, "!LeverageMacroReference: lte post check");
}
```

## Tools Used
manual review

## Recommended Mitigation Steps
To fix this issue:

1. Reverse the comparisons in the `_doCheckValueType` function for the `gte` and `lte` operators.
2. Update the function as follows:

```solidity
function _doCheckValueType(CheckValueAndType memory check, uint256 valueToCheck) internal {
    if (check.operator == Operator.skip) {
        // Early return
        return;
    } else if (check.operator == Operator.gte) {
        require(valueToCheck >= check.value, "!LeverageMacroReference: gte post check");
    } else if (check.operator == Operator.lte) {
        require(valueToCheck <= check.value, "!LeverageMacroReference: lte post check");
    } else if (check.operator == Operator.equal) {
        require(check.value == valueToCheck, "!LeverageMacroReference: equal post check");
    } else {
        revert("Operator not found");
    }
}
```




## Assessed type

Context
Unchecked return value of the low-level call() in `_depositRawEthIntoLido` .
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L38


# Vulnerability details

## Impact
The return value of the low-level call in `_depositRawEthIntoLido` is unchecked which allows the possibility of the call failing while the transaction continues without reverting.

## Vulnerability details

The call function returns a boolean value indicating whether the call was successful. However, it is important to note that this return value is not being checked in the current implementation of `_depositRawEthIntoLido`.
As a result, there is a possibility that the call wasn't successful, while the transaction continues execution without reverting.
As seen below, the return value of the call is not checked. [ZapRouterBase.sol#L38](https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L38).
 ```
    function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
        // check before-after balances for 1-wei corner case
        uint256 _balBefore = stEth.balanceOf(address(this));
        // TODO call submit() with a referral?
        // @audit the call's return value is not checked.
        payable(address(stEth)).call{value: _initialETH}("");
        // The function then continues execution without checking if the call was successful.
        uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
        return _deposit;
    }
 ```
References
https://code4rena.com/reports/2023-10-nextgen#m-11-unchecked-return-value-of-low-level-calldelegatecall

## Tools Used
Manual Review 

## Recommended Mitigation Steps
Check the return value of the low-level call.















## Assessed type

call/delegatecall
Unchecked ETH Transfer in _depositRawEthIntoLido Function
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L38


# Vulnerability details

## Proof of Concept
In the [_depositRawEthIntoLido](https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L38) function of the `ZapRouterBase` contract, there is a potentially unsafe transfer of ETH to the `stEth` contract using a low-level call. The function sends `_initialETH` without verifying if the transfer was successful. This is done using the following line:
```solidity
payable(address(stEth)).call{value: _initialETH}("");
```
The purpose of this function is to deposit raw ETH into the Lido staking protocol and return the amount of `stETH` received. However, if the call to `stEth` fails (e.g., if the stEth contract reverts), the transaction will not throw an error, and the function will continue executing, leading to an incorrect calculation of the `_deposit` value.


## Impact
If the transfer of ETH to the `stEth` contract fails, the function will not revert, resulting in an inaccurate `_deposit` value. This could lead to incorrect state updates, potential loss of funds, and overall unexpected behavior in the contract logic.


## Tools Used
Manual
## Recommended Mitigation Steps
The return value of the low-level call should be checked, and the transaction should revert if the call fails. 
```solidity
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    // check before-after balances for 1-wei corner case
    uint256 _balBefore = stEth.balanceOf(address(this));
    // call submit() with a referral if needed
    (bool success, ) = payable(address(stEth)).call{value: _initialETH}("");
    require(success, "EbtcZapRouter: ETH deposit to Lido failed");
    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    return _deposit;
}

```


## Assessed type

ETH-Transfer
Deposit success on Lido is not checked which could lead to unexpected behavior
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router/src/ZapRouterBase.sol#L38


# Vulnerability details

## Impact

The contract may behave in an unexpected manner

## Proof of concept

[https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router/src/ZapRouterBase.sol#L38](https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router/src/ZapRouterBase.sol#L38)

When a user tries to open or adjust a CDP using native ETH or WETH, the asset is converted to stETH by being deposited on Lido using the `_depositRawEthIntoLido()` internal function.

Lido has [2 ways](https://etherscan.io/address/0x17144556fd3424edc8fc8a4c940b2d04936d17eb#code) to prevent users from staking their ETH :
1. The contract is paused
2. The stake limit has been reached

If Lido ends up in one of the 2 situations above, all the deposits will essentially revert, whether it is through the use of the `fallback()` function (which eBTC uses) or through the `submit()` function.

In the current implementation, since the low-level call return value is not checked, the contract will revert directly, in an attempt to deposit the assets, which could lead to potential unexpected behaviors in the future.

*in an ideal situation, the transaction will revert later on anyway because the execution flow will try to pull stETH from the router which is not supposed to hold any token*

## Tools used

Manual analysis

## Recommended mitigation steps

There are 2 options :

1. Make sure to check the return value of the deposit to Lido in the `_depositRawEthIntoLido()` internal function

```diff
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    // check before-after balances for 1-wei corner case
    uint256 _balBefore = stEth.balanceOf(address(this));
    // TODO call submit() with a referral?
-   payable(address(stEth)).call{value: _initialETH}("");
+   (bool success,) = payable(address(stEth)).call{value: _initialETH}("");
+   require(success, "Can't stake on Lido");
    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    return _deposit;
}
```

2. Call the `submit()` function on the stETH contract instead of using its `fallback()` function which will revert in case the deposit fails.



## Assessed type

call/delegatecall
The condition check for parameters in the _getPostCheckParams function is reversed
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/LeverageZapRouterBase.sol#L257
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/LeverageZapRouterBase.sol#L165
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-protocol/packages/contracts/contracts/LeverageMacroBase.sol#L167
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-protocol/packages/contracts/contracts/LeverageMacroBase.sol#L277


# Vulnerability details

## Impact
It fails to effectively ensure that the actual collateral share is greater than or equal to the expected minimum collateral share. This could lead users to open positions that are on the verge of liquidation, resulting in the user being liquidated (which is not the user’s intention, but merely a consequence of the parameter check not functioning as intended).

## Proof of Concept
```javascript
    function _getPostCheckParams(
        bytes32 _cdpId,
        uint256 _debt,
        uint256 _totalCollateral,
        ICdpManagerData.Status _status,
        uint256 _collValidationBuffer
    ) internal view returns (PostCheckParams memory) {
        return
            PostCheckParams({
                expectedDebt: CheckValueAndType({value: _debt, operator: Operator.equal}),
                expectedCollateral: CheckValueAndType({
@>                    value:  _totalCollateral * _collValidationBuffer / BPS,
@>                    operator: Operator.gte
                }),
                cdpId: _cdpId,
                expectedStatus: _status
            });
    }


```
Let’s take the example of openCDP to see how this check is actually used.

```javascript
  function _openCdpOperation(
        bytes32 _cdpId,
        OpenCdpForOperation memory _cdp,
        uint256 _flAmount,
        TradeData calldata _tradeData
    ) internal {
        LeverageMacroOperation memory op;

        op.tokenToTransferIn = address(stETH);
        // collateral already transferred in by the caller
        op.amountToTransferIn = 0;
        op.operationType = OperationType.OpenCdpForOperation;
        op.OperationData = abi.encode(_cdp);
        op.swapsAfter = _getSwapOperations(address(ebtcToken), address(stETH), _tradeData);

        _doOperation(
            FlashLoanType.stETH,
            _flAmount,
            op,
            PostOperationCheck.openCdp,
            _getPostCheckParams(
                _cdpId,
                _cdp.eBTCToMint,
                stETH.getSharesByPooledEth(_cdp.stETHToDeposit),
                ICdpManagerData.Status.active,
                _tradeData.collValidationBufferBPS

            ),
            _cdpId
        );

        _sweepEbtc();
        _sweepStEth();
    }

```
In the _doOperation function, after opening a CDP, the actual debt and collateral are checked against the expected values