xanoutas/solidity-security-findings huggingface dataset:
Users might lose their stETH rebased reward due to the weights change
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Vulnerability Details
* Let's consider the following scenario:
  * Bob deposits 10eth with the weights:
    * stETH: `weights[0] = 90e18,`
    * rETH: `weights[1] = 5e18.`
    * sfrxETH: `weights[2] = 5e18`

  * Now, since the Lido has 80% of liquid staking market, Asymmetry Finance decides to adjust the weights and set 0% for Lido. Over the time the balance of the wstETH derivative becomes close to 0.
  * After that, Bob decides to unstake his shares, but he doesn't receive any rewards occuered on his initial 90% of stETH. Instead, he receives some eth from RocketPool and Frax, which is not comparable with the amount that he could receive back, if the weights were unchanged over the time.

* Initially Bob receives the following amount of shares:
    * X amount of shares for 90% of eth (deposited into the Lido)
    * Y amount of shares for 5% of eth (deposited into the RocketPool)
    * Z amount of shares for 5% of eth (deposited into the Frax)

</br>

* After changing the weights and the balances are also adjusted, Bob doesn't receive:
  * X amount of shares worth of wstETH
  * Y amount of shares worth of rETH
  * Z amount of shares worth of sfrxETH

* Instead, he receives accordingly to the current adjusted weights, losing rewards occured on his 90% worth of wstETH shares.


## Impact
*  Loss of user's funds.

</br>

## Tools Used
* Manual Review

</br>

## Recommended Mitigation Steps
- Short term: N/A
- Long term: The protocol design itself allows the possible scenario for happening. The problem actually, is the same, the user should not be able to receive eth from the derivative which didn't mint some _safEth initially.
Reth slippage and fee stealing
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#L171-L175


# Vulnerability details

## Impact


The Reth derivative contract calculates the maximum slippage for buying rETH from the Uniswap V3 pool by using the current price in the pool at runtime, without considering the price at which the user submitted the transaction to the mempool:
```
uint rethPerEth = (10 ** 36) / poolPrice();
uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) *
    ((10 ** 18 - maxSlippage))) / 10 ** 18);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L171-L175

Thus, a MEV bot can sandwich the `stake()` transaction, first causing slippage in its favor when the `Reth.deposit()` function is called and secondly earning fees on a price range in the Uniswap V3 pool where the transaction will occur, which the bot already knows in advance. All of this will eat up around 1% of the user's initial deposit:
```
function initialize(address _owner) external initializer {
    _transferOwnership(_owner);
    maxSlippage = (1 * 10 ** 16); // 1%
}
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L42-L46

Please note that the slippage occurs relative not to the price at which the user sent the transaction, but to the one chosen by the MEV bot when it decides that it is a favorable time for the bundle.


## Tools Used

x

## Recommended Mitigation Steps


Before submitting a `SafEth.stake()` transaction to the mempool, the user should calculate and manage the slippage. One way to do this is by passing `minOut` as an argument to the `stake()` function.
SfrxEth slippage and fee stealing
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

## Impact


The SfrxEth derivative contract calculates the maximum slippage for buying SfrxEth from curve pool by using the current price in the pool at runtime, without considering the price at which the user submitted the transaction to the mempool:
```
uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) *
    (10 ** 18 - maxSlippage)) / 10 ** 18;

IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(
    1,
    0,
    frxEthBalance,
    minOut
);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L73-L82

Thus, a MEV bot can sandwich the `SafEth.unstake()` transaction, first causing slippage in its favor when the `SfrxEth.withdraw()` function is called and secondly earning a fees by buying LP-tokens from the curve pool where the transaction will occur. All of this will eat up around 1% of the user's SfrxEth withdraw:
```
function initialize(address _owner) external initializer {
    _transferOwnership(_owner);
    maxSlippage = (1 * 10 ** 16); // 1%
}
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L36-L39

Please note that the slippage occurs relative not to the price at which the user sent the transaction, but to the one chosen by the MEV bot when it decides that it is a favorable time for the bundle.


## Tools Used

x

## Recommended Mitigation Steps


Before submitting a `SafEth.unstake()` transaction to the mempool, the user should calculate and manage the slippage. One way to do this is by passing `minOut` as an argument to the `unstake()` function.
Upgradeability completely breaks decentralization
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details




## Impact
Owner of SafEth can steal all staked funds.

## Proof of Concept
SafEth is an upgradeable ERC20 contract that handles the conversion between ETH and whatever derivatives that are implemented. But it also has access to the staked funds through the derivatives.
The staked funds are owned by the derivative contracts themselves, but SafEth has privileged access to convert and withdraw these funds to itself. Since SafEth is upgradeable the owner/upgrader of SafEth can upgrade SafEth into a contract from which he can withdraw all staked funds into his own EOA.
Furthermore, the derivatives are also upgradeable, which presents the same issue.

Details of how the staking is done and to which derivatives may very well be centralized, i.e. entrusted to an owner of an upgradeable protocol. But the ownership of the funds themselves must be securely contained in a decentralized fashion, out of reach of any central authority.

The centralizing risk in upgradeability in this case is not like, for example, the upgradeability of a normal ERC20 token. The owner of a token profits from its trading at a value. The value of the protocol resides in its functionality; it doesn't contain any funds itself. If he upgrades it inappropriately his token will instantly lose its value and the owner will lose his business. Any tokens he may have himself will be worthless. Therefore he can be entrusted with the token protocol.
However, in this case, all the value of the protocol is contained in the protocol itself; the protocol owns funds. If the protocol is centrally controlled by an owner, he also owns its value.

Since the protocol is essentially a giant wallet of funds, and that the upgradeability of it gives the owner complete access to these funds, I consider this centralization issue of high severity.

## Recommended Mitigation Steps
The access to funds must be hermetically sealed from upgradeability. Any contract which at any point owns the funds, between having been staked (not when staking), and unstaked and sent to the user, must not be upgradeable.
I think a fundamental issue here is that the unstaking is done by having the derivative contracts send the funds back to SafEth which in turn sends them to the unstaker. The derivative contracts should send the funds directly to the unstaker themselves, bypassing SafEth. Then SafEth can only direct the derivative contracts to unstake funds for the user, but cannot access the funds itself. This assumes of course that the derivative contracts are not upgradeable, and that no other holes exist where the tentacles of upgradeability can enter.
WstEth slippage and fee stealing
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact


The WstEth derivative contract calculates the maximum slippage for buying WstEth from curve pool by using the current price in the pool at runtime, without considering the price at which the user submitted the transaction to the mempool:
```
uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18;
IStEthEthPool(LIDO_CRV_POOL).exchange(1, 0, stEthBal, minOut);
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L60-L62

Thus, a MEV bot can sandwich the `SafEth.unstake()` transaction, first causing slippage in its favor when the `WstEth.withdraw()` function is called and secondly earning a fees by buying LP-tokens from the curve pool where the transaction will occur. All of this will eat up around 1% of the user's WstEth withdraw:
```
function initialize(address _owner) external initializer {
    _transferOwnership(_owner);
    maxSlippage = (1 * 10 ** 16); // 1%
}
```
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L33-L36

Please note that the slippage occurs relative not to the price at which the user sent the transaction, but to the one chosen by the MEV bot when it decides that it is a favorable time for the bundle.

## Tools Used

x

## Recommended Mitigation Steps


Before submitting a `SafEth.unstake()` transaction to the mempool, the user should calculate and manage the slippage. One way to do this is by passing `minOut` as an argument to the `unstake()` function.
Spot UniswapV3 pricing for rETH when staking in SafEth can lead to loss of user funds
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#L170-L185
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L215
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L228-L242


# Vulnerability details

## Impact

An attacker can craft a set of transactions so that when they are depositing funds in the `SafEth` contract, using the `stake()` function, they can understate the value of existing deposits (`preDepositPrice` value), while overstating the value of their deposit (`totalStakeValueEth` value). This vulnerability happens because of how `ethPerDerivative(..)` is calculated for rETH. Specifically, if users cannot deposit directly into the rETH contract, then the `Reth` logic will revert to pricing rETH using the UniswapV3 pool spot price, and also the call to `deposit()` will result in swapping through the same UniswapV3 pool. 

To take advantage of this, an attacker can first decrease the spot price of rETH in the pool, which will result in a lower `preDepositPrice` value when the attacker calls the `stake()` function of the `SafEth` contract. Then, later in the same call when `deposit()` is called for the rETH staking derivative, it will get rETH by swapping ETH -> rETH in the UniswapV3 pool. This swapping will increase the value of the rETH that was received by the swap. The subsequent `ethPerDerivative(..)` function call will then award the attacker an excess amount for their deposit, by overstating the value of the received rETH. This is invalid behavior, as the price of rETH should not be changing atomically in the same transaction.

This is medium severity because an attacker is able to drain significant funds from the `SafEth` contract. However, this can only occur in specific scenarios, based on maxSlippage, whether the rETH contract is accepting deposits, the distribution and depth of the UniswapV3 liquidity.

## Proof of Concept

POC including preliminary state and steps (foundry test). This POC was done using block number 16936327:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// utilities
import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";

// project files
import "../interfaces/rocketpool/RocketStorageInterface.sol";
import "../interfaces/uniswap/IUniswapV3Factory.sol";
import "../interfaces/uniswap/IUniswapV3Pool.sol";
import "../interfaces/uniswap/ISwapRouter.sol";
import "../interfaces/IWETH.sol";

import "../SafEth/SafEth.sol";
import "../SafEth/derivatives/Reth.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

// using interface to interact w/ proxy contract for simplicity
interface ISafEth {
    function stake() external payable;
    function unstake(uint256 _safEthAmount) extern
In `stake()` function shares increase exponentially for the same amount of deposit as the totalSupply() increases, causing the loss for the initial stakers relative to later ones.
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-L101


# Vulnerability details

## Impact
Stake function don not allocate the safEth according to the totalShares, instead as more user deposit, later depositers get higher share of the value as compared to ealy depositers.
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L63-L101

## Proof of Concept
Consider Alice, Bob and Jenny want to stake their eth.

Note: Ignoring slippage for the easy calculation here
1. First Alice deposit 10 eth to the protocol, and gets the 10 safEth in return and totalSupply becomes 10.
2. Next Bob deposits 10 eth to the protocol, than BOB will get 20 safEth for the 10 eth deposit that are way greater than alice's share.
3. Next Jenny deposits 10 eth to the protocl, this time jenny gets more share than all of the previous depositers: whooping 50 shares.

So now all three of them staked 10 eth but totalSupply() is 80, out of which 10 is with Alice, 20 with Bob and 50 with jenny. Jenny getting bigger of the pie so on unstaking jenny will get the maximum share though eveyone deposited the same amount of eth.

Following test can be run to check for the balances:

```solidity
  it("Should deposit 10 eth from 3 account alice bob and jenny and print their minted share", async function () {
    const strategy = await getLatestContract(strategyContractAddress, "SafEth");
    const userAccounts = await getUserAccounts();

    const user1StrategySigner = strategy.connect(userAccounts[0]);
    const user2StrategySigner = strategy.connect(userAccounts[1]);
    const user3StrategySigner = strategy.connect(userAccounts[2]);

    //stake from all
    await user1StrategySigner.stake();
    await user2StrategySigner.stake();
    await user3StrategySigner.stake();

    // print balances
    const user1Balance = await strategy.balanceOf(userAccounts[0].address);
    const user2Balance = await strategy.balanceOf(userAccounts[1].address);
    const user3Balance = await strategy.balanceOf(userAccounts[2].address);

  });
```

Note: I manually reviewed this calculation, was running out of time to run the tests themselves.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Change the share minting mechanism that does the fair distribution of share according to the ethereum deposited.
An attacker can manipulate the preDepositvePrice to steal from other users.
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details


## Impact

The first user that stakes can manipulate the total supply of sfTokens and by doing so create a rounding error for each subsequent user. In the worst case, an attacker can steal all the funds of the next user.

## Proof of Concept

When the first user enters totalSupply is set to 1e18 on [L79](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L79):
```solidity
if (totalSupply == 0)
            preDepositPrice = 10 ** 18; // initializes with a price of 1
        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
```
But the user can immediately unstake most of his safETH such that totalSupply <<  1e18. The attacker can then transfer increase the underlying amount by transferring derivative tokens to the derivative contracts. 

For subsequent users, the preDepositPrice will be heavily inflated and the calculation of mintAmount on [L98](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L98):
```solidity
uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
```
can be very inaccurate. In the worst case it rounds down to 0 for users that deposit value that is less than the value that the attacker transferred in. 

In the following POC the attacker steals all of the second user's deposit. The attacker first deposits 100 ETH and immediately removes all but 1 wei. The attacker then transfers 10 wsETH to the WstEth contract. When the second user enters with 1.5 ETH no additional safETH are minted since the minAmount is rounded down to 0. The attacker has all of the safTokens and can withdraw 100% of deposits.


Create a new test file with the following content to run the POC.
```solidity
import { SafEth } from "../typechain-types";

import { ethers, upgrades, network } from "hardhat";
import { expect } from "chai";
import {
  getAdminAccount,
  getUserAccounts,
  getUserBalances,
  randomStakes,
  randomUnstakes,
} from "./helpers/integrationHelpers";
import { getLatestContract } from "./helpers/upgradeHelpers";
import { BigNumber } from "ethers";

import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20.json";
import { RETH_MAX, WSTETH_ADRESS, WSTETH_WHALE } from "./helpers/constants";

describe.only("SafEth POC", function () {
  let safEthContractAddress: string;
  let strategyContractAddress: string;
  // create string array
  let derivativesAddress: string[] = [];
  let startingBalances: BigNumber[];
  let networkFeesPerAccount: BigNumber[];
  let totalStakedPerAccount: BigNumber[];

  before(async () => {
    startingBalances = await getUserBalances();
    networkFe
And all this assembly shall know that the OWNER SafEth not with `derivatives`: for the derivative is the OWNER'S, and he will `rebalanceToWeights` it into his EOA.
Audit: 2023-03-asymmetry-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L138-L155
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L165-L175
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L182-L195


# Vulnerability details



## Impact
The owner of SafEth can at any time steal all staked funds.

## Proof of Concept
[`SafEth.addDerivative()`](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L182-L195) allows the owner to add any derivative contract, such as one where he can withdraw all `IDerivative.deposit`-ed funds.
[`SafEth.adjustWeights()`](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L165-L175) allows the owner to set the sole non-zero weight to his new derivative contract such that all funds will be directed there. This alone enables him to steal all subsequently staked funds. Note also that a big pending stake can be front-run with the above steps.
But most critically, [`SafEth.rebalanceToWeights()`](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L138-L155) allows the owner to withdraw all funds and redeposit them all to his derivative contract, for a complete rug pull.

The amount of funds eventually staked through SafEth will likely be larger than any other economic incentive the owner could possibly have to keep SafEth running. This rug pull opportunity may therefore be considered all but irresistible to the owner. Since this rug is also close to trivial to pull, I consider this to be of high severity, despite being a simple centralization issue.

## Recommended Mitigation Steps
It is commented that `rebalanceToWeights` probably isn't going to be used anyway. Removing this prevents the worst of the rug pull. Note also that rebalancing can be sped up by a temporary setting of exaggerated weights, if this is needed, which may be coded directly in `stake` such that funds are staked where they are lacking according to their weights.
The possibility for the owner to redirect stakes into his derivative by adjusting the weights could be mitigated by affecting any change in weights with a time delay. Then stakers will at least have time to discover that a derivative they disapprove of has been added.