xanoutas/solidity-security-findings huggingface dataset:
_totalAssets() in Vault.sol calls the wrong function to get amount of assets managed by YieldVault
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L800-L802
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1176


# Vulnerability details

## Impact

The accounting of assets in Vault.sol will not be accurate. Exchange Rate Calculation will be affected.

## Proof of Concept

In Vault.sol, maxWithdraw(address(this)) is used to calculate the amount of assets managed by the YieldVault. 

```
   * @dev The total amount of assets managed by this vault is equal to
   *      the amount of assets managed by the YieldVault + the amount living in this vault.
  function _totalAssets() internal view returns (uint256) {
    return _yieldVault.maxWithdraw(address(this)) + super.totalAssets();
  }
```

maxWithdraw() in ERC4626 actually takes in the amount of shares that the owner has and calls `_convertToAssets()`. Calling maxWithdraw() and passing in address(this), the vault address, does not really work because the vault address do not hold the shares, the user does.

```
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }
```

Since the function `_totalAssets()` intends to return the asset amount in the yield vault. it should call `totalAssets()` to fetch the asset amount in the yieldVault instead of calling maxWithdraw().

```
    function totalAssets() public view virtual returns (uint256) {
        return _asset.balanceOf(address(this));
    }
```

This affects the exchangeRate calculation because `_currentExchangeRate()` also uses `maxWithdraw()`, but it should use `totalAssets()` instead.

```
    uint256 _withdrawableAssets = _yieldVault.maxWithdraw(address(this));
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

Use totalAssets() instead to calculate the amount of assets in the Yield Vault.


## Assessed type

ERC4626
Missing Deadline parameter in liquidate() of Vault.sol
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L550-#L556


# Vulnerability details

## Impact
Due to absence of deadline parameter , pending transactions to be maliciously executed at a later point.

## Proof of Concept
The liquidate() in `Vault.sol` is basically a swap between prize tokens and Vault shares. Without the presence of a deadline parameter , a User can unknowingly perform bad swaps.
1) Alice wants to liquidate 100 prize tokens with Vault shares at an exchange rate of 2:1.
2)The transaction is submitted to the mempool, however, Alice chose a transaction fee that is too low for miners to be interested in including her transaction in a block. The transaction stays pending in the mempool for extended periods, which could be hours, days, weeks, or even longer.
3) When the average gas fee dropped far enough for Alice's transaction to become interesting again for miners to include it, her liquidation process will be executed. But by this time the exchange rate changed from 2:1 to 3:1 causing bad swaps.

## Tools Used
Manual Review
## Recommended Mitigation Steps
Introduce a deadline parameter to the liquidate().


## Assessed type

Other
Vault will drain if `_yieldVault` is fee-on-transfer token
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L518
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L532


# Vulnerability details

## Impact
On every withdraw/redeem, assets in the vault will decrease faster than expected and finally, the vault will drain.

## Proof of Concept
`Vault._withdraw` assumes `_yieldVault.withdraw(_assets, address(this), address(this));` will return the same amount of tokens as `_assets`
but this is not the case if `_yieldVault` is a fee-on-transfer token. 
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1026-L1027

## Tools Used
Manual review

## Recommended Mitigation Steps
Calculate the difference of the asset and transfer the amount to the receiver.
```diff
+ uint256 assetBefore = IERC20(asset()).balanceOf(address(this)); 
_yieldVault.withdraw(_assets, address(this), address(this));
+ uint256 assetAfter = IERC20(asset()).balanceOf(address(this));
SafeERC20.safeTransfer(IERC20(asset()), _receiver, assetAfter - assetBefore);
```


## Assessed type

Token-Transfer
An attacker can install himself as a DrawManager and steal funds in the withdrawReserve() functions in PrizePool.sol
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L299-L306
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L278
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L335


# Vulnerability details

## Impact
The drawManager parameter can be set in the constructor (https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L278). However, if drawManager = 0 is passed to the constructor, it can be set later in setDrawManager() (https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L299- L306). The drawManager parameter in setDrawManager() is only updated once.

If you don't set drawManager in the constructor, an attacker can get ahead and set any address in setDrawManager(). The administrator will not be able to change it in the future.

The drawManager parameter is used in
modifier onlyDrawManager() {
     if (msg.sender != drawManager) {
       revert CallerNotDrawManager(msg.sender, drawManager);
     }
     _;
}
at the time of checking access to the withdrawReserve() and closeDraw() functions.

Suppose someone managed to deposit funds into the prize pool via increaseReserve(). This could result in the funds in withdrawReserve() being stolen by an attacker (https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L335).

## Tools Used
Manual review

## Recommended Mitigation Steps
Only set the value of drawManager in the constructor. Or set the onlyOwner check for setDrawManager().


## Assessed type

Access Control
Tier odds in TieredLiquidityDistributor are incorrect
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/abstract/TieredLiquidityDistributor.sol#L83-L200


# Vulnerability details

## Impact
The following points are stated in the docs:

1. The highest standard prize tier is the most common prize: occurring every single draw.
2. The canary tier has odds of occurring daily (as if it were the highest tier)

Therefore, both the highest standard tier and the canary tier should have odds of 1, however at the moment only the canary tier has odds of 1. The result is that the protocol is not distributing prizes as intended.

## Proof of Concept
The issue with the odds calculations can be seen in the `TieredLiquidityDistributor.sol` contract. Below is a short snippet for the tier odds when there are 3 tiers:

```
  SD59x18 internal constant TIER_ODDS_0_3 = SD59x18.wrap(2739726027397260);
  SD59x18 internal constant TIER_ODDS_1_3 = SD59x18.wrap(52342392259021369);
  SD59x18 internal constant TIER_ODDS_2_3 = SD59x18.wrap(1000000000000000000);
```

The canary tier odds are 1 as intended, however the highest normal prize tier odds are incorrect. If the tier odds were being calculated correctly, we should see the last two odds for each number of tiers being 1 (i.e. `1000000000000000000`).

## Tools Used
Manual review

## Recommended Mitigation Steps
Since the `TierCalculationLib.getTierOdds` only seems to be used by the `GenerateConstants` script, I suggest modifying the script used to generate the constants that are placed in `TieredLiquidityDistributor.sol`:

```
diff --git a/script/generateConstants.s.sol b/script/generateConstants.s.sol
index fe1d4ed..fb9f90a 100644
--- a/script/generateConstants.s.sol
+++ b/script/generateConstants.s.sol
@@ -38,16 +38,24 @@ contract GenerateConstants is Script {
     MAX_TIERS = 15;
     // Precompute the odds for each tier
     for (uint8 numTiers = MIN_TIERS; numTiers <= MAX_TIERS; numTiers++) {
-      for (uint8 tier = 0; tier < numTiers; tier++) {
+      for (uint8 tier = 0; tier < numTiers - 1; tier++) {
         console.log(
           "SD59x18 internal constant TIER_ODDS_%d_%d = SD59x18.wrap(%d);",
           uint256(tier),
           uint256(numTiers),
           uint256(
-            SD59x18.unwrap(TierCalculationLib.getTierOdds(tier, numTiers, GRAND_PRIZE_PERIOD_DRAWS))
+            SD59x18.unwrap(TierCalculationLib.getTierOdds(tier, numTiers - 1, GRAND_PRIZE_PERIOD_DRAWS))
           )
         );
       }
+      console.log(
+          "SD59x18 internal constant TIER_ODDS_%d_%d = SD59x18.wrap(%d);",
+          uint256(numTiers - 1),
+          uint256(numTiers),
+          uint256(
+            SD59x18.unwrap(TierCalculationLib.getTierOdds(numTiers - 1, numTiers, GRAND_PRIZE_PERIOD_DRAWS))
+          )
+        );
     }
   }
 }

```


## Assessed type

Math
Reorg attack in VaultFactory
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/VaultFactory.sol#L67-L78


# Vulnerability details

## Impact
The deployVault function deploys a clone contract using create, where getting the address depends only on the VaultFactory nonce.

Reorganization can occur in all EVM chains. In Ethereum, where pooltogether is currently deployed, this is not "super common" but still happening, being the last one less than a year ago:
https://decrypt.co/101390/ethereum-beacon-chain-blockchain-reorg

An issue can arise when users rely on getting an address beforehand, or try to deploy a clone of a position with the same address on different EVM chains, any funds sent to the new clone could potentially be withdrawn by someone else. In general, this can lead to the theft of users' funds.

An example of a similar error: https://code4rena.com/reports/2023-01-rabbithole#m-01-questfactory-is-suspicious-of-the-reorg-attack

## Proof of Concept
Imagine that Alice deploys a clone of a position and then sends funds to it. Bob sees that the network block is being reorganized and calls deployVault. Thus, a clone of the position is created with the address to which Alice sends funds. Alice's transactions are then executed and Alice transfers the funds to Bob's position contract.

## Tools Used
Manual review

## Recommended Mitigation Steps
Deploy Vault via create2 with salt which includes msg.sender and _asset address.


## Assessed type

Context
H-04 Unmitigated
Audit: 2024-06-renzo-mitigation-findings
Severity: HIGH

# Lines of code




# Vulnerability details

## Details
As per the communication with C4 staff, a mitigation that introduces a new vulnerability needs a separate submission with `unmitigated` label.

See the report submitted with the title:
- Honest users can lose funds when calling the `claim()` function.
Honest users can lose funds when calling the `claim()` function.
Audit: 2024-06-renzo-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/Renzo-Protocol/Contracts/blob/c5d62fc0319fca154f6e0e1c16c6c532f861e1c7/contracts/Withdraw/WithdrawQueue.sol#L290-L293


# Vulnerability details

## Original Issue Summary
- [H-04: Withdrawals logic allows MEV exploits of TVL changes and zero-slippage zero-fee swaps](https://github.com/code-423n4/2024-04-renzo-findings/issues/326)

In the original issue, three scenarios are explained where MEV and/or malicious actors can exploit the fact that the withdrawal amount is calculated at withdrawal request submission time instead of at withdrawal claim time. Any small deviation of the amount from `withdraw` to `claim` can be exploited by MEV.

## Mitigation
- [Mitigation H-04](https://github.com/Renzo-Protocol/Contracts/pull/111)

This mitigation proposes calculating the redeem value at the `withdraw()` function and at the `claim()` function. 

## Comments
This mitigation introduces another issue that harms honest users, which we will describe in the next few paragraphs.

## Newly Introduced Vulnerability Description
The mitigation proposes the creation of the following function:
```javascript!
    function _calculateAmountToRedeem(
        uint256 _amount,
        address _assetOut
    ) internal view returns (uint256 _amountToRedeem) {
        // calculate totalTVL
        (, , uint256 totalTVL) = restakeManager.calculateTVLs();

        // Calculate amount to Redeem in ETH
        _amountToRedeem = renzoOracle.calculateRedeemAmount(_amount, ezETH.totalSupply(), totalTVL);

        // update amount in claim asset, if claim asset is not ETH
        if (_assetOut != IS_NATIVE) {
            // Get ERC20 asset equivalent amount
            _amountToRedeem = renzoOracle.lookupTokenAmountFromValue(
                IERC20(_assetOut),
                _amountToRedeem
            );
        }
```
and calling this function twice instead of once. `_calculateAmountToRedeem()` is called once during the `withdraw()` call and once during the `claim()` call. This mitigation aims to mitigate MEV/large arb opportunties.

However, this mitigation will introduce a problem which will impact honest users and make them lose (a portion of) their funds.

## Proof of Concept
Assume Alice is a honest user.
- Alice calls `withdraw()` with `100e18 ezETH`.
    - The amount that Alice should receive is calculated by calling the newly added `_calculateAmountToRedeem()` function.
- Due to the mitigation, when Alice calls `claim()`, `_calculateAmountToRedeem()` is called again.
    - Here lays the problem. Inside `_calculateAmountToRedeem()`, the amount to redeem gets calculated using the current total TVL by calling `restakeManager.calculateTVLs()`.
    - However, this total TVL can be drastically different from the TVL that Alice initially calculated during the `withdraw()` call. Note that this does not need the interference of a malicious party for the TVL to drop. `restakeManager.calculateTVLs()` uses multiple factors to calculate the TVL and for
H-04 Unmitigated
Audit: 2024-06-renzo-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/main/contracts/Withdraw/WithdrawQueue.sol#L220-L224


# Vulnerability details

## Original Vulnerability
The [original vulnerability report](https://github.com/code-423n4/2024-04-renzo-findings/issues/326) described a generic issue in the protocol due to the usage of oracles to calculate the mint and redeem exchange rates for ezETH. The root cause was the fact that the exchange rate for deposits/withdrawals was calculated on the spot based on oracle prices and the current amount of assets in the protocol, without any additional fees.

The report described 3 different scenarios in which oracle prices were exploited to extract value from the protocol.

The exploitation of [changes in the amount of collateral](https://github.com/code-423n4/2024-04-renzo-findings/issues/413) in the protocol in a similar manner (e.g. by sandwiching protocol reward deposits) was not explicitly mentioned in the report, but can be seen as sharing a common root cause as described above.

It is worth pointing out that a large amount of findings were duplicated under this finding as per the judge's criteria of a common root cause being "the pricing of assets upon withdrawal, which are only actually converted upon claiming".

This is quite different from the root cause highlighted in the original report and so it is hard to say what the appropriate mitigation should have been.

We will assume the issue being mitigated here is the primary finding.

## Mitigation
The recommended mitigation of giving users the worse exchange rate between that calculated at the time of withdrawal initiation and at the time of claiming was applied in PR [#111](https://github.com/Renzo-Protocol/Contracts/pull/111).

An additional suggestion to implement "a rate limit or a short delay on deposits with similar protection" was not implemented. Also worth noting is that the lack of fees on deposits/withdrawals was cited as a concern multiple times in the original report, but was not suggested nor implemented as a mitigation.

## Analysis
The implemented mitigation does not protect against scenario 1, which is still possible exactly as originally described with the added risk that if the price of ezETH drops, the attacker may suffer a loss (unrelated to the arbitrage) on claiming. Since Renzo is designed such that the price of ezETH is generally increasing, the attack can be assumed to still be profitable.

Furthermore, while it protects against short-term oracle price manipulation as described in scenario 2 of the original report, the same attack is possible if the manipulation is carried out both at the time of withdrawal initiation and at the time of claiming. While this increases the cost of the attack, it remains viable.

It also does not fully protect against scenario 3, since while the arbitrageur _will_ take a loss on claiming due to the ezETH price decreasing;
- the decrease in the ezETH price will be of smaller magnitude than t
Calling `WithdrawQueue.claim()` can change the `RestakeManager`'s mint/redeem rate enabling value extraction exploit
Audit: 2024-06-renzo-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/Renzo-Protocol/Contracts/blob/7dabe5478ec6dbf084135737bb0d88960e80b63e/contracts/Withdraw/WithdrawQueue.sol#L225


# Vulnerability details

When a user queues a withdrawal request in [`WithdrawQueue.withdraw()`](https://github.com/Renzo-Protocol/Contracts/blob/7dabe5478ec6dbf084135737bb0d88960e80b63e/contracts/Withdraw/WithdrawQueue.sol#L215), the corresponding ezETH amount is not immediately burned, but rather locked in the `WithdrawQueue` until the user claims the withdrawal after the cooldown period. The collateral amount to be redeemed in the "happy path" of Renzo suffering no losses is [calculated](https://github.com/Renzo-Protocol/Contracts/blob/7dabe5478ec6dbf084135737bb0d88960e80b63e/contracts/Withdraw/WithdrawQueue.sol#L225) based on the ezETH to collateral ratio and the price of the asset being withdrawn at the time of queuing the withdrawal.

If this ratio increases (the price of ezETH decreases) and/or the price of the asset decreases, the user will still be sent the amount calculated initially instead of the new, larger amount and their locked ezETH will be burned, effectively increasing the mint/redeem exchange rate during claiming.

## Impact
Users or arbitrageurs can sandwich claim transactions to profit from the undue swing in the ezETH mint/redeem rates, effectively extracting value from the protocol without any oracle price movements happening between their actions. This is shown in the runnable PoC below.

Additionally, users depositing before the claim will be minted more ezETH than they deserve, and those withdrawing will be able to redeem less collateral.

## Proof of Concept
The below Foundry POC demonstrates the issue by performing the following sequence of steps:
1. Alice and Bob deposit 100 stETH and 100 cbETH respectively, receiving 100 ezETH each.
2. Bob initiates a withdrawal of 100 ezETH for cbETH, which gets queued.
3. At any point in the week following Bob's withdraw request, collateral prices change by 20%
4. An exploiter who is aware of the issue mints 100 stETH worth of ezETH 
5. Bob claims their withdrawal, immediately increasing the redeem rate with this only action
6. The exploiter redeems their ezETH, again for stETH, banking a profit of 9 ezETH.

It's important to note that no oracle price moved in between the exploiter's actions, so the exploiter is not profiting from price changes, but rather from the unintended change in mint/redeem rate caused by the `claim` call.

The PoC can be run in Foundry by using the setup and mock infra provided here -> https://gist.github.com/3docSec/a4bc6254f709a6218907a3de370ae84e
```solidity
pragma solidity ^0.8.19;

import "contracts/Errors/Errors.sol";
import "./Setup.sol";

contract MR is Setup {

    function testMR() public {
        uint t0 = block.timestamp;

        WithdrawQueueStorageV1.TokenWithdrawBuffer[] memory buffers = new WithdrawQueueStorageV1.TokenWithdrawBuffer[](2);

        buffers[0] = WithdrawQueueStorageV1.TokenWi