xanoutas/solidity-security-findings huggingface dataset:
ETH funds of the derivative contracts can be drained by flash-loan attacks
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Fund loss of derivative contracts via flash-loan attacks.

## Proof of Concept
The function `unstake()` is the exit point of the `SafETH.sol` contract. This function burns the users corresponding `SafETH` tokens and pays back the ETH to the users. It gets the `_safEthAmount` from the user and calculates the value of the corresponding asset in ETH and pays to the user. The mechanism is based on the difference of the balance of the contract before and after withdrawing the ETH from the derivative contracts. For a detailed analysis, it's better to look at the 'for-loop' which calculates the amount needed to be withdrawn from each derivative contract:

```
        for (uint256 i = 0; i < derivativeCount; i++) {
            // withdraw a percentage of each asset based on the amount of safETH
            uint256 derivativeAmount = (derivatives[i].balance() *
                _safEthAmount) / safEthTotalSupply;
            if (derivativeAmount == 0) continue; // if derivative empty ignore
            derivatives[i].withdraw(derivativeAmount);
        }
```
This function is well-written and the loop above guarantees that the user cannot trick the contract and ensures that the user will get his corresponding assets. However, there is a problem with the calculation of the `derivativeAmount` which provides a flash-loan-based attack for an attacker.
Although the `_safEthAmount` is rational and cannot be tricked, the balance of the derivative may help the user to increase `derivativeAmount` intentionally. The function `balance()` which is equal to the `balanceOf()` function of the ERC20 token in the derivative contract, may be inflated locally. In other words, with the help of the out-of-thin-air assets, a large amount of ETH can be transferred to the user which leads to a fund loss of a derivative contract.
In the rest of the code, after a successful ETH withdrawal from the derivative contract, the function calculates the difference between the `SafETH.sol` contract balance and sends the Ether to the `msg.sender`.

Now let's see a possible flash-loan-based attack:

1 - Bob stakes 0.5 Ether which is the minimum amount of staking
2 - He then creates a transaction that starts by getting a large amount of flash loan/swap
3 - He exchanges/wraps the flash loan for one of the staked derivative underlying tokens
4 - He transfers his tokens to the derivative contracts
5 - After transferring the tokens to the derivative contract, the `balanceOf` of the derivative's underlying token increases.
6 - He now calls `unstake()` with a fair amount provided for `_safEthAmount`
7 - He gets the corresponding Ethers and pays back the loan at the end of the transaction

Inside the for loop, the `balance()` is inflated temporarily and makes the `derivativeAmount` increase drastically. Thus, the Ethers of `address(this).balance` is sent to t
frxETH price manipulation
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The frxETH derivative uses `price_oracle` of the curve pool which can be used to manipulate the slippage limits

## Proof of Concept

### Background

The `price_oracle()` in the Curve pool is an exponential moving average that exponentially converges from an old value to the last trade value. This is manipulatable in certain conditions. Someone can write a contract that flash-loans a large amount of token A (comparable to the amount of token A in a pool), swaps it for token B in the pool, swap the token B back to token A and repay the flash-loan. This would mean that the last price would significantly differ from the actual price. If there are no transactions on the pool for a while after this, then the pool's `price_oracle()` would converge to a value close to this last price and hence, very different from the actual tokenA/tokenB price.

One key point here is that this kind of price manipulation doesn't require too many funds at risk (since the flashloan is paid up). The maximum loss for the exploiter would just be the fee on the flashloan and the two swaps.

An example of this manipulation for Curve v2 pools can be found in this repo
https://github.com/shuklaayush/brownie-curve-v2

### Vulnerability

- Do a huge flashloan trade for frxETH/ETH pool to crash the frxETH price. Wait for some time for the `price_oracle` to converge to the crashed value
- Any subsequent withdrawal tx will have a low slippage limit for the frxETH -> ETH conversion and can be sandwiched 

## Tools Used

## Recommended Mitigation Steps

Use a proper oracle
Can bypass max slippage in withdraw function in SfrxEth
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The max slippage in the ```SfrxEth``` derivative contract can by bypassed by sending FRXETH to the derivative contract before withdrawing, then, inside the withdraw call, the exchange function on the CRV pool is called with an inaccurate min_out value. This is inaccurate because the min_out is calculated with the amount passed by the SafEth contract, but the amount sent for trading is the FRXETH balance of the contract.
It is easy to imagine a flash loan attack where a user can add FRXETH liquidity to the CRV pool to cause the slippage to increase, then by some loan on SafEth (which is easily possible because SafEth is a normal ERC20) they can sell themselves FRXETH for a cheaper price and come out with more at the end of the attack.
This attack effectively makes it so that protocol sells FRXETH for cheaper than its actual value.

## Proof of Concept
The proof of concept is simple (did not demonstrate attack, but I did show slippage can easily exceed 1%):
1) deposit ETH to ```SfrxEth```
2) Then get FRXETH in some fashion (I just minted it for sake of the PoC)
3) Unbalance curve pool by depositing a large amount of FRXETH
4) Send some FRXETH to ```SfrxEth``` contract
5) Call withdraw and observe that slippage has effectively been exceeded

To do this I edited and added some interfaces
added interface for IFrxEth
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

// made interface to use mint function for testing
interface IFrxEth {
 function minter_mint(address m_address, uint256 m_amount) external;
 function balanceOf(address account) external view returns (uint256);
 function transfer(address to, uint256 amount) external returns (bool);
 function approve(address spender, uint256 amount) external returns (bool);
}
```
added some functions to curvepool interface
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// https://etherscan.io/address/0x8301AE4fc9c624d1D396cbDAa1ed877821D7C511#code
interface ICrvEthPool {
 function exchange(
     uint256 i,
     uint256 j,
     uint256 dx,
     uint256 min_dy
 ) external payable returns (uint256);

 function exchange_underlying(
     uint256 i,
     uint256 j,
     uint256 dx,
     uint256 min_dy
 ) external;

 function get_virtual_price() external view returns (uint256);

 function price_oracle() external view returns (uint256);

 function get_dy(
     uint256 i,
     uint256 j,
     uint256 dx
 ) external view returns (uint256);

 function add_liquidity(
     uint256[2] calldata amounts,
     uint256 min_mint_amount
 ) external returns (uint256);
}
```

Then I added this test case to the ```Derivatives``` test suite inside of ```SafEth.test.ts```
```js
it("Can manipulate max slippage on SfrxEth contract", async () => {
     const sfrxEth = derivatives[1] // get SfrxEth contract

     //
Inflation attack
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The protocol is vulnerable to the well know inflation attack/first deposit bug. The attacker can steal assets from other user's stakes.

Reference cases:

[Overview of the Inflation Attack](https://mixbytes.io/blog/overview-of-the-inflation-attack)
[Fist deposit bug](https://github.com/code-423n4/2023-01-ondo-findings/issues/247#issuecomment-1399228254)

### The Bug

The share calculation uses the ERC20 balance of the derivates in the derivates pool. And attacker can transfer ERC20 tokens to inflate the balance. So that underlyingValue&preDepositPrice will be inflated. And the mint amount `mintAmount` will be deflated.

```
        // Getting underlying value in terms of ETH for each derivative
        for (uint i = 0; i < derivativeCount; i++)
            underlyingValue +=
                (derivatives[i].ethPerDerivative(derivatives[i].balance()) *
                    derivatives[i].balance()) /
                10 ** 18;

        uint256 totalSupply = totalSupply();
        uint256 preDepositPrice; // Price of safETH in regards to ETH
        if (totalSupply == 0)
            preDepositPrice = 10 ** 18; // initializes with a price of 1
        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
        ....

        // mintAmount represents a percentage of the total assets in the system
        uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
        _mint(msg.sender, mintAmount);
```

Steps to attack:

1. Once the contracts has been deployed, the attacker mints a small shares.

2. Then the attacker does a plain underlying token transfer to the derivate contract, artificially inflating the derivatives[i].balance() value.

Due to the above steps, during the next legitimate user deposit, the mintAmount value for
the user will become less than 1 and essentially be rounded down to 0 by Solidity.
Hence the user gets 0 share against his deposit and the SafETH entire supply is held by the Attacker.

The Attacker can then simply unstake his SafETH balance for the entire underlying token balance of the entire protocol.

## Proof of Concept


## Tools Used
Manual Review

## Recommended Mitigation Steps

Mint a same amount of SafETH to ZERO address on the first stake.
DENIAL OF SERVICE AFTER STAKING 200ETH THE 22ND TIME.
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Every stake function call reverts after the first user stakes 200ETH 22 times.

## Proof of Concept
Any other stake function call fails after the 22nd stake of 200ETH fails causing denial of service.

Consider this scenario
- the smart contract is deployed
- Bob deposits 4400ETH by calling the stake function 22 times.
- Alice wants to deposit 0.5Eth but the transaction reverts.
- Every other users try to stake ETH but their transaction reverts.

```Solidity
/* eslint-disable new-cap */
import { network, ethers } from "hardhat";
import { expect } from "chai";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { SafEth } from "../typechain-types";

import { initialUpgradeableDeploy } from "./helpers/upgradeHelpers";

describe("Proof of concept", function () {
  let admin: SignerWithAddress;
  let alice: SignerWithAddress;
  let safEthProxy: SafEth;
  let initialHardhatBlock: number;

  const resetToBlock = async (blockNumber: number) => {
    await network.provider.request({
      method: "hardhat_reset",
      params: [
        {
          forking: {
            jsonRpcUrl: process.env.MAINNET_URL,
            blockNumber,
          },
        },
      ],
    });

    safEthProxy = (await initialUpgradeableDeploy()) as SafEth;
    const accounts = await ethers.getSigners();
    admin = accounts[0];
    // The second account [1] provided
    alice = accounts[1];
  };

  before(async () => {
    const latestBlock = await ethers.provider.getBlock("latest");
    initialHardhatBlock = latestBlock.number;
    await resetToBlock(initialHardhatBlock);
  });

  describe("Denial of Service", function () {
    it("it reverts after trying to stake 200ETH for the 23th time (0 =< i < 23 )", async function () {
      const depositAmount3 = ethers.utils.parseEther("200");

      for (let i = 0; i < 22; i++) {
        await safEthProxy.connect(alice).stake({ value: depositAmount3 });
      }
      // Denial of service after the above number of staking
      // makes every other stake function call after the 22nd, 200ETH staking to fail
      await safEthProxy
        .connect(admin)
        .stake({ value: ethers.utils.parseEther(".5") });
      expect(true).eq(true);
    });
  });
});

```

Below is the error occuring after running the above test.
```
 Error: VM Exception while processing transaction: reverted with reason string 'Too little received'
      at <UnrecognizedContract>.<unknown> (0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45)
      at Reth.swapExactInputSingleHop (contracts/SafEth/derivatives/Reth.sol:101)
      at Reth.deposit (contracts/SafEth/derivatives/Reth.sol:177)
      at <UnrecognizedContract>.<unknown> (0x1f53e116c31f171e59f45f0752aec5d1f5aa3714)
      at SafEth.stake (contracts/SafEth/SafEth.sol:81)
      at <UnrecognizedContract>.<unknown
Wrong slippage calculation during `withdraw` can cause sandwich attacks in SfrxEth Derivative. 
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Vulnerability Details:
Where : [`withdraw() - SfrxEth derivative`](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L74-L82)

Slippage is calculated using the spot price of the curve pool from which swap is being done, which can be easily manipulated for sandwich attacks. 

## Proof of Concept
- Possibility of sandwich attacks, as when user submits  high value transaction in mempool. 
- Attacker keeps track and sells massive amount of FRX from FRX-ETH Curve Pool. 
- As `minOut` is calculated from the same inflated pool, the slippage holds no value under this circumstance. 
```
        uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) *
            (10 ** 18 - maxSlippage)) / 10 ** 18;

        IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(
            1,
            0,
            frxEthBalance,
            minOut
        );
```
- The user gets less amount of Eth then deserved. 
- Attacker immediately buys FRX in the next transaction or directly above the user's transaction and makes profit from the arbitrage. 
 
## Impact
##### These attack has two implications: 
1. User get less Eth than deserved. 
2. Huge losses could occur during [`rebalanceToWeights()`](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L138)

## Tools Used
Manual Review

## Recommended Mitigation Steps 
In such scenarios, it is recommended that slippage should not be calculated from the spot price of the pool.
RETH price manipulation
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
RETH derivative uses uniswap v3 spot price which can be manipulated to steal from the pool

## Proof of Concept

The `ethPerDerivative` call in the `underlyingValue` calculation uses the total amount currently deposited in the liquid staking derivative.
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L73

This means that for RETH, when deposits are high, this would return `poolPrice()` which is the uniswap v3 spot price. This price can be manipulated
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L211-L216
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L228-L242

1. Flashloan RETH and crash RETH/ETH uniswap v3 spot price
2. Stake ETH to get more shares minted (since `ethPerDerivative` for RETH would be small)
3. Unstake shares to get more ETH than deserved 

## Tools Used

## Recommended Mitigation Steps

Use an oracle for RETH price
The withdraw() function of Reth.sol derivative contract doesn't work as intended at all, it seems
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/b3861c62e22b4c8df2a19d25cad8d98eaaf23a9e/contracts/SafEth/derivatives/Reth.sol#L105-L114


# Vulnerability details

## Impact
Definitely sends incorrect amount of ETH back to user, thats IF anything is sent back to owner contract at all. However, if the burn function works correctly, then the Reth contract balance before receiving user's funds via withdraw function, is NOT zero. The contract balance is shared by other users deposit/withdraw transactions...
Assume that the burn() function/method of the RocketTokenRETHInterface(rethAddress()) works correctly, which I could not check(where is it?), so if it works correctly, it sends ETH back to the Reth contract. Cool. Since there's no accounting at the user level, as well as problem of race-conditions, share contract balances, etc, and without checking if the burn function was successful, the next statement is executed, which sends the total ETH balance of the Reth contract(could be zero?) to the SafEth owner contract. IF there's ETH balance, which means it works differently from the other two derivative contracts, then it sends all of it, which does not belong to the user. And if the burn function returns nothing/fails, then balance will be sent to owner contract anyway. At least medium risk? But probably high risk?

## Proof of Concept

## Tools Used
Manual only.

## Recommended Mitigation Steps
Add require check that the amount parameter is <= than user's safEth balance.
Check that burn function exists, and is correctly implemented?
Add require check after burn function to confirm, before sending funds back to owner contract/user.
And, sending the entire contract balance back to the owner contract, can not be correct at all.
Probably needs similar calculations as which are used by withdraw functions in the other two derivative contracts. But at least there should be mappings to distinguish between user deposits/balances.
Forgot to update weights for each derivative (edited)
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

 https://github.com/code-423n4/2023-03-asymmetry/blob/d016bef40dc745b6a16e18d83bd4f7d776245903/contracts/SafEth/SafEth.sol#L63-L101
https://github.com/code-423n4/2023-03-asymmetry/blob/d016bef40dc745b6a16e18d83bd4f7d776245903/contracts/SafEth/SafEth.sol#L108-L129


# Vulnerability details

## Impact
Give the wrong weights for each derivative if we don't update `weights` after minting and burning.

## Proof of Concept
https://github.com/code-423n4/2023-03-asymmetry/blob/d016bef40dc745b6a16e18d83bd4f7d776245903/contracts/SafEth/SafEth.sol#L63-L101
no update of `weights` after minting the derivative to keep track of weights for each derivative.

https://github.com/code-423n4/2023-03-asymmetry/blob/d016bef40dc745b6a16e18d83bd4f7d776245903/contracts/SafEth/SafEth.sol#L108-L129
no update of `weights` after burning the derivative to keep track of weights for each derivative.

## Tools Used
VS code

## Recommended Mitigation Steps
Update `weights`
A temporary issue shows in the staking functionality which leads to the users receiving less minted tokens.
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L63-L101
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#L211-L216


# Vulnerability details

## Derivative Reth prices 
A quick explanation of the issue causing it, the problem is based on the function "ethPerDerivative" in the Reth derivative.
As you can see two statements can be triggered here, the first one "if (poolCanDeposit(_amount))" checks if the given amount + the pool balance isn't greater than the maximumDepositPoolSize and that the amount is greater than the minimum deposit in the pool. Second statement is meant to return a poolPrice which is slightly more than the regular one, because it's used in order to swap tokens in Uniswap and therefore the price per token is overpriced.

```solidity
function ethPerDerivative(uint256 _amount) public view returns (uint256) {
        if (poolCanDeposit(_amount))
            return
                RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18);
        else return (poolPrice() * 10 ** 18) / (10 ** 18);
    }
```
``` solidity
// poolCanDeposit() returns:
      return
            rocketDepositPool.getBalance() + _amount <=
            rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize() &&
            _amount >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit();
```

Below you can see the regular price returned in the first statement - 1063960369075232250:


<img width="417" alt="Screenshot 2023-03-27 at 8 53 34" src="https://user-images.githubusercontent.com/112419701/227852484-9bf0144d-820d-414c-8cb9-e1fb44f5c806.png">

Below you can see the pool price from the second statement, supposed to be used only when a swap is made.

```solidity
else return (poolPrice() * 10 ** 18) / (10 ** 18);

// poolPrice calculates and returns
uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
        return (sqrtPriceX96 * (uint(sqrtPriceX96)) * (1e18)) >> (96 * 2);

// uint160 sqrtPriceX96 = 81935751724326368909606241317
// return (sqrtPriceX96 * (uint(sqrtPriceX96)) * (1e18)) >> (96 * 2);
// return 1069517062752670179 (pool price)

// The function "ethPerDerivative" for the else statement return (poolPrice() * 10 ** 18) / (10 ** 18);
// Which will be - 1069517062752670179
```

Difference between the regular price and the pool price:

```
regular price - 1063960369075232250
pool price -    1069517062752670179
```

## Quick Overview

What can result to users receiving less minted tokens?

The first thing the staking function does is calculating the derivative underlyingValue. This issue occurs on the Reth derivative, as we can see the staking function calls "ethPerDerivative" to get the price, but takes as account the whole Reth balance of the derivative contract. 

For example let's say the derivative Reth holds 200e18. The pool has free