xanoutas/solidity-security-findings huggingface dataset:
Incorrect parameter in `withdraw` function
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L74


# Vulnerability details

## Impact

An incorrect parameter is used in the `withdraw` [function](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L74) in `SfrxEth.sol`. The `_amount` variable is used when the `frxEthBalance` variable should be used to calculate `minOut`. The amount that gets swapped at the `FRX_ETH_CRV_POOL_ADDRESS` is the `frxEthBalance`, not the `_amount` which is not necessarily the same.

In the worst case the current code can set the minimum amount too high which would cause a revert in the `IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange` function call and prevent withdrawals.

## Tools Used
Manual Review

## Recommended Mitigation Steps

Use `fexEthBalance` in the place of `_amount` when determining the minimum amount of tokens out.
Missing critical check of amount minted tokens in stake() -> deposit()
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L94-L106
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L73-L81


# Vulnerability details

## Impact
No `require` to ensure that SfrxEth or WstEth is minted when calling in SafEth.sol stake() -> IDerivative.deposit() function

## Proof of Concept
In the `deposit()` function in Reth.sol has a `require` statement to ensure that the token was actually minted. But there is no such check in other derivatives. But variables for such a check are created.
It can lead to losing some part of the tokens that should be minted when the user stake his ETH.
Consequently, the user would receive fewer ETH after executing the unstake() function.
```Solidity
    File: contracts\SafEth\derivatives\Reth.sol

    uint256 rethBalance1 = rocketTokenRETH.balanceOf(address(this));
    rocketDepositPool.deposit{value: msg.value}();
    uint256 rethBalance2 = rocketTokenRETH.balanceOf(address(this));
    require(rethBalance2 > rethBalance1, "No rETH was minted");
    uint256 rethMinted = rethBalance2 - rethBalance1;
    return (rethMinted);

```

But in other derivatives, there is no such `require` statement.

```Solidity
File: contracts\SafEth\derivatives\SfrxEth.sol
    function deposit() external payable onlyOwner returns (uint256) {
        IFrxETHMinter frxETHMinterContract = IFrxETHMinter(FRX_ETH_MINTER_ADDRESS);
        uint256 sfrxBalancePre = IERC20(SFRX_ETH_ADDRESS).balanceOf(address(this));
        frxETHMinterContract.submitAndDeposit{value: msg.value}(address(this));
        uint256 sfrxBalancePost = IERC20(SFRX_ETH_ADDRESS).balanceOf(address(this));
        return sfrxBalancePost - sfrxBalancePre;
    }
```
```Solidity
File: contracts\SafEth\derivatives\WstEth.sol
    function deposit() external payable onlyOwner returns (uint256) {
        uint256 wstEthBalancePre = IWStETH(WST_ETH).balanceOf(address(this));
        // solhint-disable-next-line
        (bool sent, ) = WST_ETH.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
        uint256 wstEthBalancePost = IWStETH(WST_ETH).balanceOf(address(this));
        uint256 wstEthAmount = wstEthBalancePost - wstEthBalancePre;
        return (wstEthAmount);
    }
```

## Tools Used
Manual Review
## Recommended Mitigation Steps
Add `require` statement to ensure that the token was actually minted
Swap deadline for `Reth` derivative cannot be specified and used for staking
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L156-L204
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L83-L102


# Vulnerability details

## Impact
When calling the following `Reth.deposit` function, `uint256 amountSwapped = swapExactInputSingleHop(W_ETH_ADDRESS, rethAddress(), 500, msg.value, minOut)` can be executed. Calling the `Reth.swapExactInputSingleHop` function below then calls `ISwapRouter(UNISWAP_ROUTER).exactInputSingle(params)`.

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L156-L204
```solidity
    function deposit() external payable onlyOwner returns (uint256) {
        ...
        if (!poolCanDeposit(msg.value)) {
            uint rethPerEth = (10 ** 36) / poolPrice();

            uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
                ((10 ** 18 - maxSlippage))) / 10 ** 18);

            IWETH(W_ETH_ADDRESS).deposit{value: msg.value}();
            uint256 amountSwapped = swapExactInputSingleHop(
                W_ETH_ADDRESS,
                rethAddress(),
                500,
                msg.value,
                minOut
            );

            return amountSwapped;
        } else {
            ...
        }
    }
```

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L83-L102
```solidity
    function swapExactInputSingleHop(
        address _tokenIn,
        address _tokenOut,
        uint24 _poolFee,
        uint256 _amountIn,
        uint256 _minOut
    ) private returns (uint256 amountOut) {
        IERC20(_tokenIn).approve(UNISWAP_ROUTER, _amountIn);
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
            .ExactInputSingleParams({
                tokenIn: _tokenIn,
                tokenOut: _tokenOut,
                fee: _poolFee,
                recipient: address(this),
                amountIn: _amountIn,
                amountOutMinimum: _minOut,
                sqrtPriceLimitX96: 0
            });
        amountOut = ISwapRouter(UNISWAP_ROUTER).exactInputSingle(params);
    }
```

When `ISwapRouter(UNISWAP_ROUTER).exactInputSingle(params)` is called, `params` does not include a deadline for the swap. This means that a staking transaction, which needs the `Reth.deposit` function to be called, can remain pending in the mempool for a long time, such as due to the accompanying transaction fee being too low. Later, when a miner finally includes such staking transaction, the price of ETH in terms of rETH can become much lower, which would also cause `minOut` in the `Reth.deposit` function to become much smaller, at that moment comparing to the time when such transaction was just sent. Because the user cannot directly control her or his own slippage, a much lower `minOut` means that it is more likely that such staking transaction can be sandwich attacked succe
Division before multiplication truncate minOut and incurs heavy precision loss and result in insufficient slippage protection
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L173
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L74


# Vulnerability details

## Impact

When Calcuting the minOut before doing trade,

Division before multiplication truncate minOut and incurs heavy precision loss, then very sub-optimal amount of the trade output can result in loss of fund from user because of the insufficient slippage protection

## Proof of Concept

In the current implementation,

slippage can be set by calling

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L206

```solidity
/**
	@notice - Sets the max slippage for a certain derivative index
	@param _derivativeIndex - index of the derivative you want to update the slippage
	@param _slippage - new slippage amount in wei
*/
function setMaxSlippage(
	uint _derivativeIndex,
	uint _slippage
) external onlyOwner {
	derivatives[_derivativeIndex].setMaxSlippage(_slippage);
	emit SetMaxSlippage(_derivativeIndex, _slippage);
}
```

which calls the corresponding derivative contract

### Case 1

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L58

```solidity
/**
	@notice - Owner only function to set max slippage for derivative
	@param _slippage - new slippage amount in wei
*/
function setMaxSlippage(uint256 _slippage) external onlyOwner {
	maxSlippage = _slippage;
}
```

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L173

calling

```solidity
uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
	((10 ** 18 - maxSlippage))) / 10 ** 18);

IWETH(W_ETH_ADDRESS).deposit{value: msg.value}();
uint256 amountSwapped = swapExactInputSingleHop(
	W_ETH_ADDRESS,
	rethAddress(),
	500,
	msg.value,
	minOut
);
```

As we can see, the division before multiplication happens in the line of code

```solidity
uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
	((10 ** 18 - maxSlippage))) / 10 ** 18);
```

for example, if maxSlippage is 10 ** 17

(10 ** 18 - 10 ** 17) / (10 ** 18) = 0

then minOut is 0, slippage control is disabled because of the division before multipcation.

### Case 2

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L51

```solidity
/**
	@notice - Owner only function to set max slippage for derivative
*/
function setMaxSlippage(uint256 _slippage) external onlyOwner {
	maxSlippage = _slippage;
}
```

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L74

the divison before multipilcation happens below

```solidity
uint256 minOut =
User should be able to set the slippage amount willing to pay
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L63


# Vulnerability details

## Impact
The impact of this finding is that users may be required to pay higher transaction fees than necessary due to the absence of an adjustable slippage feature. This could discourage users from using the SafEth contract and negatively impact its adoption.

## Proof of Concept
There is a slippage settings by the protocol but it might not be as user's expected amount.

## Tools Used
Manual

## Recommended Mitigation Steps
Add a slippage tolerance feature to the contract that allows users to set the maximum amount of slippage they are willing to pay for their transactions.

When a user submits a transaction, the contract should compare the transaction price to the current market price and determine if the transaction falls within the user's specified slippage tolerance.

If the transaction price falls outside the user's specified slippage tolerance, the contract should reject the transaction and notify the user.

The contract should also provide clear instructions and guidance on how to set the slippage tolerance to ensure users can use the feature effectively.
Incorrect minOut calculation in SfrxEth.withdraw()
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L74-L75


# Vulnerability details

## Impact
Final value of mintOut will be lower or higher according to the SFRX_ETH/FRX_ETH price ratio. This can result in higher slippage where user can loss funds. Else in other case the slippage will be low and transaction gets reverted unexpectedly

## Proof of Concept
1.The minOut should be 1% less than `(frxEthBalance * ethPerDerivative)`
2.But instead of `frxEthBalance`, the `_amount` param is used to calculate the minOut. The `_amount` param represents the redeemed `sfrxEth` balance.

## Tools Used
Manual code review

## Recommended Mitigation Steps
Use the below formula instead of previous one
`uint256 minOut = (((ethPerDerivative(_amount) * frxEthBalance) / 10 ** 18) * (10 ** 18 - maxSlippage)) / 10 ** 18;`
Potential reentrancy in unstake function
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L124


# Vulnerability details

## Impact
there is a potential reentrancy vulnerability in the unstake function.

After the user's safETH tokens are burned, the function sends ETH to the user's address using the call method. If the receiving address is a contract and it has a fallback function that makes a call back to the unstake function, it can potentially cause reentrancy.

This is because the call method forwards any remaining gas to the receiving contract, which can be used to execute further code. If this code includes a call back to the unstake function, it can result in unexpected behavior.


## Tools Used
manual inspection

## Recommended Mitigation Steps

use reentrancy guard
Hardcoded slippage can lead to user's transactions being front run 
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L44
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L38
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L35


# Vulnerability details

## Vulnerability details

## Impact
Since all the main function that the user execute implement slippage, attackers can front run any user transaction since the slippage amount is set to  1% on all the lines of code listed above. This can lead to sandwich attacks.

## Proof of Concept

When the user is using main protocol function here and there, there is no slippage protection. Therefore any main calls could be exploited for sandwich attacks or other MEV exploits.

The scenario would be:
A authorized actor calls stake() function, leading to a swap of say X token to Y token.

Then while the transaction is in the mempool and the blockchain gas fees are high due the 1% users Tx may stay a while in the mempool, it is exploited for example like in https://medium.com/coinmonks/defi-sandwich-attack-explain-776f6f43b2fd

## Recommended Mitigation Steps

Consider adding a function argument that the user can customize from the front end if he wants to change the slippage value.
Hardcoded poolFee in `deposit()` Function Cause Liquidity Depth Issue for Swap
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L180


# Vulnerability details

## Impact
In `deposit` function we are using the hardcoded value of poolFee as 500 which limits the ability of liquidity providers to select the appropriate fee tier, potentially reducing the returns for liquidity providers.

```solidity
IWETH(W_ETH_ADDRESS).deposit{value: msg.value}();
uint256 amountSwapped = swapExactInputSingleHop(
    W_ETH_ADDRESS,
    rethAddress(),
    500,  //@audit hardcode value
    msg.value,
    minOut
);
```

```solidity
 IWETH(W_ETH_ADDRESS).deposit{value: msg.value}();
            uint256 amountSwapped = swapExactInputSingleHop(
                W_ETH_ADDRESS,
                rethAddress(),
                fee,
                msg.value,
                minOut
            );
            
```


## Tools Used
Manual Review
## Recommended Mitigation Steps
Donot hardcode the fee, use some other optimum technique
`Reth.poolCanDeposit` checks only two out of three conditions
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L146-L150


# Vulnerability details




## Impact
Staking is blocked.

## Proof of Concept
When staking the `deposit` of each derivative is called. `Reth.deposit` deposits in the Rocket Pool directly if possible. To decide if this is possible it uses `Reth.poolCanDeposit` which [returns](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L146-L150) whether the deposit pool size after depositing exceeds the maximum size and whether the deposited amount is less than the minimum deposit size:
```solidity
return
    rocketDepositPool.getBalance() + _amount <=
    rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize() &&
    _amount >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit();
```
However `RocketDepositPool.deposit` also [requires that deposits be enabled](https://etherscan.io/address/0x2cac916b2A963Bf162f076C0a8a4a8200BCFBfb4#code#F7#L103):
```solidity
// Check deposit settings
RocketDAOProtocolSettingsDepositInterface rocketDAOProtocolSettingsDeposit = RocketDAOProtocolSettingsDepositInterface(getContractAddress("rocketDAOProtocolSettingsDeposit"));
require(rocketDAOProtocolSettingsDeposit.getDepositEnabled(), "Deposits into Rocket Pool are currently disabled");
require(msg.value >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit(), "The deposited amount is less than the minimum deposit size");
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
require(rocketVault.balanceOf("rocketDepositPool").add(msg.value) <= rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize(), "The deposit pool size after depositing exceeds the maximum size");
```

This means that deposits may fail despite being directed to the deposit pool instead of getting rETH via Uniswap, which means that staking reverts.

## Recommended Mitigation Steps
Add ` && rocketDAOProtocolSettingsDeposit.getDepositEnabled()` to the return of `Reth.poolCanDeposit`.
sFrxEth may revert on redeeming non-zero amount
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L61-L65
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L118


# Vulnerability details




## Impact
Unstaking is blocked.

## Proof of Concept
When unstaking the `withdraw` of each derivative is called. `SfrxEth.withdraw` calls [`IsFrxEth(SFRX_ETH_ADDRESS).redeem(_amount, address(this), address(this));`](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L61-L65). This function may revert if `_amount` is low due to the following line in `redeem` (where `_amount` is `shares`):
[`require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");`](https://etherscan.io/token/0xac3e018457b222d93114458476f3e3416abbe38f#code#L708)
`previewRedeem(uint256 shares)` [returns `convertToAssets(shares)`](https://etherscan.io/token/0xac3e018457b222d93114458476f3e3416abbe38f#code#L753) which is the shares scaled by the division of total assets by total supply:
[`shares.mulDivDown(totalAssets(), supply)`](https://etherscan.io/token/0xac3e018457b222d93114458476f3e3416abbe38f#code#L733).
So if `_amount == 1` and total assets in sFrxEth is less than its total supply, then `previewRedeem(shares) == 0` and `redeem` will revert. This revert in `SfrxEth.withdraw` causes a revert in `SafEth.unstake` at [L118](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L118), which means that funds cannot be unstaked.

`_amount` may be as low as 1 when the weight for this derivative has been set to 0 and funds have adjusted over time through staking and unstaking until only 1 remains in the SfrxEth derivative. Instead of just being depleted it may thus block unstaking.

## Recommended Mitigation Steps
In `SfrxEth.withdraw` check if `IsFrxEth(SFRX_ETH_ADDRESS).previewRedeem(_amount) == 0` and simply return if that's the case.
Failed withdrawal of one derivative blocks unstaking
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L118


# Vulnerability details



## Impact
Unstaking becomes impossible and staked funds stuck.

## Proof of Concept
[Unstaking depends on the success of the `withdraw` call for each derivative](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L118). If for any reason withdrawals are halted from a derivative, nothing can be unstaked on SafEth. If a derivative is permanently out of withdrawal, all staked funds in SafEth will be stuck.

## Recommended Mitigation Steps
Allow for withdrawals to fail and withdraw only from the other derivatives. How this is withdrawn may have to be set by the owner to account for the possibility of lost stakes and that these losses be shared equally.
unstake can be paused into ransom
Audit: 2023-03-asymmetry-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L241-L244


# Vulnerability details




## Impact
Owner can extort stakeholders for their share.

## Proof of Concept
[The owner can pause unstaking at will](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L241-L244). Holding the stakeholders hostage for long enough they will begin to be willing to sell off their shares more cheaply on the market, which the owner can buy and later unstake himself for their full value.
Since the owner can pause unstaking indefinitely at no cost to himself, he will be almost guaranteed to be able to buy it back cheaper. The alternative for the stakeholders is to lose everything, so they will be forced to sell it cheaply to him.

## Recommended Mitigation Steps
It makes little sense to pause unstaking for a diversification of stakes. Any pausability in unstaking should be deferred to the staking protocols themselves.