xanoutas/solidity-security-findings huggingface dataset:
`_totalWithdrawn` VALUE DOES NOT INCLUDE THE `_fee` AMOUNT THUS INTRODUCING ACCOUNTING ERROR
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L459
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L473
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L830-L833
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L743-L746
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L312


# Vulnerability details

## Impact
 In the `PrizePool.ClaimPrize` function is used to claim the rewards of the verified winner. Here when sending the Prize amount to the winner a `_fee` amount is deducted from it as shown below:

    uint256 amount = tierLiquidity.prizeSize - _fee;

And this `amount` is transferred to the `winner` of the draw by calling the `_transfer` functin as shown below:

    _transfer(_prizeRecipient, amount);

In the `PrizePool._transfer` function the `_totalWithdrawn` state variable is updated with the prize amount as shown below:

    _totalWithdrawn += _amount;

But the issue here is that the `_totalWithdrawn` is updated without the `_fee` amount. Hence the `_totalWithdrawn`  does not indicate the total liquidity that has been withdrawn (in the form of prizes) from the beginning.

The `_totalWithdrawn` is used in the `PrizePool._accountedBalance` function to calculate the number of tokens that have accounted for. But this calculation is wrong since the `_fee` amount is excluded in the `_totalWithdrawn` value. 

Since the `_accountedBalance` is used inside the `Vault.liquidate` function for a conditional check in calculating the `Prize Pool` tokens to be transferred to the `accumulator`, the above error could propagate to break the accounting of the critical functions and their conditional checks.

## Proof of Concept

```solidity
    uint256 amount = tierLiquidity.prizeSize - _fee;
```

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L459

```solidity
    _transfer(_prizeRecipient, amount);
```

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L473

```solidity
  function _transfer(address _to, uint256 _amount) internal {
    _totalWithdrawn += _amount;
    prizeToken.safeTransfer(_to, _amount);
  }
```

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L830-L833

```solidity
  function _accountedBalance() internal view returns (uint256) {
    Observation memory obs = DrawAccumulatorLib.newestObservation(totalAccumulator);
    return (obs.available + obs.disbursed) - _totalWithdrawn;
  }
```

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5
mintYieldFee function does not check for the maxMint amount.
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Theoretically, it is possible to mint more than the `maxMint` amount using the `mintYieldFee` function in the `Vault` contract.

## Proof of Concept
The functions in `Vault` contract like `mint`, `mintWithPermit` call the `_beforeMint` function which checks whether `_shares` are greater than `maxMint` amount and reverts if it's indeed the case. But, the `mintYieldFee` amount does not check this. The `yieldFeeTotalSupply` is a unit256 which gets subtracted with `shares`, but the maxMint only allows for uint96. So theoretically, it is possible to mint more shares than maxMint, because shares are a uint256 value, not uint96 in the function.

But, there is a scenario where the protocol should be mindful. There is no limit on the decimals of the asset tokens that are used as the vaults can be created permissionless by anyone. And shares also have the same amount of decimals. So, `yieldFeeTotalSupply` could accumulate in amount (even if the underlying value is less) to be greater than max(uint96) value, which makes it possible for a share greater than `maxMint`(max(uint96)) amount to be subtracted from `yieldFeeTotalSupply` and hence possible to mint shares greater than max(uint96) value.

## Tools Used
Manual review

## Recommended Mitigation Steps
It is recommended that the protocol add the `maxMint` check in the `mintYieldFee` function.





## Assessed type

Invalid Validation
precision loss due to division before multiplication
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L766-L773


# Vulnerability details

## Impact
The result will be wrong due to division before multiplication precision issues.

## Proof of Concept
This issue is similar to https://github.com/code-423n4/2023-05-ajna-findings/issues/367 Ajna Contest.
A good example of this Vulnerability is in the Detector Documentation https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply


## Recommended Mitigation Steps
All the multiplication should be performed in step 1 and then division at the end.


## Assessed type

Math
`drawManager` CAN BE SET TO A MALICIOUS ADDRESS
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L278-L281
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#L348
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L335-L342


# Vulnerability details

## Impact

In the `PrizePool.constructor` the `drawManager` state variable is set as follows:

    drawManager = params.drawManager;

But it accepts even if the `params.drawManager == address(0)`. There is no input validation for the address(0) as well. 

The `PrizePool.setDrawManager` function is used to set the `drawManager` if not already set. But the problem here is that there is no access control for this function and any one can call this. A malicious user can front run the `valid` `drawManager` assignment transaction and set a malicious address as the `drawManager`. The other issue is once the `drawManager` is set it can not be updated due to the following conditional check. Hence the contract will have to redeployed.

    if (drawManager != address(0)) { //@audit-issue - can be front run by a malicious user
      revert DrawManagerAlreadySet();
    }

Hence the following two functions controlled by the `onlyDrawManager` modifier will be vulnerable to attacks, since the `drawManager` is a malicious address now.

1. PrizePool.withdrawReserve can withdraw tokens from the `reserve` to any address given in the `_to` parameter. Hence this function will be vulnerable.

2. `PrizePool.closeDraw()` function can close the current open draw. This function is only callable by the `drawManager`. Hence a malicious user can get control of this function thus making this function vulnerable.

## Proof of Concept

```solidity
    drawManager = params.drawManager;
    if (params.drawManager != address(0)) {
      emit DrawManagerSet(params.drawManager);
    }
```

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

```solidity
  function setDrawManager(address _drawManager) external {
    if (drawManager != address(0)) {
      revert DrawManagerAlreadySet();
    }
    drawManager = _drawManager;

    emit DrawManagerSet(_drawManager);
  }
```

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

```solidity
  function closeDraw(uint256 winningRandomNumber_) external onlyDrawManager returns (uint16) {
```

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L348

```solidity
  function withdrawReserve(address _to, uint104 _amount) external onlyDrawManager {
    if (_amount > _reserve) {
      reve
In important libraries of PoolTogether, the pow() function of PRBMath is used, which exhibits inconsistent return values
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

- https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/libraries/DrawAccumulatorLib.sol#L406-L407
- https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/libraries/TierCalculationLib.sol#L26


# Vulnerability details

The `DrawAccumulatorLib.sol` and `TierCalculationLib.sol` libraries inherit a version of `PRBMath` that contains a critical vulnerability in the `pow()` function, which can return inconsistent values. This vulnerability is of great importance to the PoolTogether protocol, as the `pow()` function is used in the computation of `TierCalculationLib.getTierOdds` and `DrawAccumulatorLib.computeC`. Recently, another protocol has also experienced the same bug, and the creators of the `PRBMath` have acknowledged this situation. Here is the corresponding [link](https://github.com/sablier-labs/v2-core/pull/432). Due to time constraints, we were unable to thoroughly address certain rounding errors with `mul` and `div` functions of SD59x18. However, these errors have been corrected in PRBMath [V4](https://github.com/PaulRBerg/prb-math/releases/tag/v4.0.0).

## Impact
PRBMath `pow()` function can return inconsistent values

## Proof of Concept
[Proof of the bug acknowledgment by the creator of the PRBMath](https://github.com/sablier-labs/v2-core/pull/432)

`This PR makes four significant changes:`
- Upgrades to PRBMath v4, which contains the fix for https://github.com/cantinasec/sablier/issues/2 and https://github.com/cantinasec/sablier/issues/1.
- Bumps all other submodules to their most recent versions.
- Increases the minimum Solidity pragma to v0.8.19, since this is a requirement of PRBMath V4.

## Tools Used
Manual review

## Recommended Mitigation Steps
To mitigate this issue, please update the contracts to `0.8.19` and upgrade the `PRBMath` to version `V4`.


## Assessed type

Math
Yield fee is minted out of thin air
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L573-L575
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L584


# Vulnerability details

## Impact
Any call to `liquidation` gives some share of the liquidated tokens to the yield fee recipient. These shares are not taken from the liquidator, but are newly minted.That means these tokens are essentially minted out of thin air.

## Proof of Concept
The user liquidates the tokens and mint some fees to the Vault. Now the vault is undercollateralized and no more [deposits](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L375-L377) or [mints](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L383-L385) are allowed. 
Overtime the yield generated will slow collateralize the protocol again, but since some operations are forbidden this is an temporary dos attack and the protocol will receive an reputational damage


## Tools Used
Manual review

## Recommended Mitigation Steps

Deduct the yieldFee from the liquidator fee to keep the vault fully collateralized.

```diff
+   fee = _amountOut * FEE_PRECISION) / (FEE_PRECISION - _yieldFeePercentage) - _amountOut;
	if (_yieldFeePercentage != 0) {
      _increaseYieldFeeBalance(
+       fee
-       (_amountOut * FEE_PRECISION) / (FEE_PRECISION - _yieldFeePercentage) - _amountOut
      );
    }

    uint256 _vaultAssets = IERC20(asset()).balanceOf(address(this));

    if (_vaultAssets != 0 && _amountOut >= _vaultAssets) {
      _yieldVault.deposit(_vaultAssets, address(this));
    }

+    _mint(_account, _amountOut - fee);
-    _mint(_account, _amountOut);

    return true;
  }

```





## Assessed type

Other
Attacker can frontrun deployVault to deploy at the same address
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
Vaults are created from the factory via CREATE1, an attacker can frontrun deployVault to deploy at the same address but with different config. If the deployed chain reorg, a different vault might also be deployed at the same address.

## Proof of Concept
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/VaultFactory.sol#L67-L78
1. Bob setup a bot to monitor the mempool when PT deploy a new vault
2. Bob's bot saw a deployment by PT at 0x1234, fire a tx to deposit immediately
3. Alice frontrun PT's deployment by deploying a malicious vault at 0x1234
4. Bob's transaction ended up deposited into Alice's malicious vault

## Recommended Mitigation Steps
Use CREATE2 and the vault config as salt.


## Assessed type

MEV
High Prizes might not be claimed
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-claimer/blob/57a381aef690a27c9198f4340747155a71cae753/src/Claimer.sol#L210
https://github.com/GenerationSoftware/pt-v5-claimer/blob/57a381aef690a27c9198f4340747155a71cae753/src/Claimer.sol#L173


# Vulnerability details

## Impact
PoolTogether V5 delegates the task of claiming the prizes to a network of `Claimers` that are incentivized to claim prizes for a share of the prize won.
The fees the Claimant receives is calculated based on an Dutch Auction, but the limited by the minimum prize of the price pool

fee = min( fee in auction, % maxFeePortionOfPrize * minPrize)

When the gas costs exceed the `minPrize` the solver has not incentives to claim the price. 
Notice that the number of prizes only adjusts *after* very few prizes are claimed.

## Proof of Concept
Gas prices rise well above the `minPrize`. The highest prize is suddenly drawn that day. The solvers have no incentive to claim the prize and the winner does not monitor the contract. As a result, no one claims the top prize. The protocol suffers a loss of reputation.

## Tools Used
Manual review, VS Code

## Recommended Mitigation Steps
The problem is that the incentives are not exactly aligned. The user is willing to pay anything up to his price p to receive his price. 
But the maximum that the solvers receives is the minimum price. We can align the two incentives by using each user's price as an upper bound.

```diff
function _computeMaxFee(uint8 _tier, uint8 _numTiers) internal view returns (uint256) {
    uint8 _canaryTier = _numTiers - 1;
    if (_tier != _canaryTier) {
      // canary tier
-     return _computeMaxFee(prizePool.getTierPrizeSize(_canaryTier - 1));
+	  return _computeMaxFee(prizePool.getTierPrizeSize(_tier));
	} else {
      return _computeMaxFee(prizePool.getTierPrizeSize(_canaryTier));
    }
  }

```

Since we already validate the inputs in [`claimPrize`](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L417-L419) the attacker can not claim more than the prize is worth.




## Assessed type

Other
The type conversion overflow caused quantity calculation error
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/libraries/TierCalculationLib.sol#L17-L27


# Vulnerability details

## Impact

The code inventory is in the case of a large number of type conversions, but there is no overflow detection, and overflow may occur when a large type is converted to a small type, resulting in calculation errors.

## Proof of Concept

```solidity
  function estimatedClaimCount(
    uint8 _numberOfTiers,
    uint16 _grandPrizePeriod
  ) internal pure returns (uint32) {
    uint32 count = 0;
    for (uint8 i = 0; i < _numberOfTiers; i++) {
      count += uint32(
        uint256(
          unwrap(sd(int256(prizeCount(i))).mul(getTierOdds(i, _numberOfTiers, _grandPrizePeriod)))
        )
      );
    }
    return count;
  }

  function getTierOdds(
    uint8 _tier,
    uint8 _numberOfTiers,
    uint16 _grandPrizePeriod
  ) internal pure returns (SD59x18) {
    SD59x18 _k = sd(1).div(sd(int16(_grandPrizePeriod))).ln().div(
      sd((-1 * int8(_numberOfTiers) + 1))
    );

    return E.pow(_k.mul(sd(int8(_tier) - (int8(_numberOfTiers) - 1))));
  }
```

This is the most obvious example, where _grandPrizePeriod is uint16 but converted to int16, and _tier and _numberOfTiers are uint8 but converted to int8.
When they are greater than 1/2 of the corresponding type, they overflow into negative numbers, resulting in a calculation error.

## Tools Used

Manual review

## Recommended Mitigation Steps

Detect overflow before converting type



## Assessed type

Math
Dynamic maxWithdraw causes previous users loss funds
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

The vault contract is designed to interact with any standard ERC4626 vault and relies on the ratio of `_withdrawableAssets(maxWithdraw)` and `_totalSupplyAmount` when calculating exchangeRate.
For maxWithdraw, the ERC4626 standard describes it this way: 

> Maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
> MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0.

When the vault restricts partial withdrawal, `maxWithdraw` decreases but does not set to 0. At this time, exchangeRate calculation decreases: the shares minted by the same amount of asset increase, while the same amount of shares withdrawn decreases, resulting in capital loss.
When users withdraw funds at this time, they will suffer losses, and the lost funds will be permanently stuck in the underlying vault and cannot be withdrawn, because of the burn of shares.

## Proof of Concept

```solidity
  function _currentExchangeRate() internal view returns (uint256) {
    uint256 _totalSupplyAmount = _totalSupply();
    uint256 _totalSupplyToAssets = _convertToAssets(
      _totalSupplyAmount,
      _lastRecordedExchangeRate,
      Math.Rounding.Down
    );

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

    if (_withdrawableAssets > _totalSupplyToAssets) {
      _withdrawableAssets = _withdrawableAssets - (_withdrawableAssets - _totalSupplyToAssets);
    }

    if (_totalSupplyAmount != 0 && _withdrawableAssets != 0) {
      return _withdrawableAssets.mulDiv(_assetUnit, _totalSupplyAmount, Math.Rounding.Down);
    }

    return _assetUnit;
  }

  function _isVaultCollateralized() internal view returns (bool) {
    return _currentExchangeRate() >= _assetUnit;
  }
```

ExchangeRate calculations depend on maxWithdraw. When the underlying vault restricts withdrawals, maxWithdraw and exchangeRate decrease, resulting in manipulated ratios.
Although the code is protected by `_isVaultCollateralized`, but which will pass if exchangeRate greater than assetUnit.

## Tools Used

Manual review

## Recommended Mitigation Steps

exchangeRate should not rely on variable maxWithdraw


## Assessed type

ERC4626
ALL WINNER CLAIMS CAN BE DoS BY A SINGLE MALICIOUS WINNER
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L618-L629
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1053
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1068


# Vulnerability details

## Impact
The `Vault.claimPrizes` function is used to `claim prizes` for the `winners` of the prizes. This is done by iterating through two nested `for` loops. One for loop iterates through the number of winners and other for loop iterates through the `_prizeIndices`.

Inside these nested loops the `Vault._claimPrize()` internal function is called. In the `Vault._claimPrize()` function, the `hooks.useBeforeClaimPrize` and `hooks.implementation.afterClaimPrize` hooks of a particular winner can be called. These hooks are set by the `winner` himself calling the `Vault.setHooks` function.

Hence a malicious `winner` can `revert` the transaction inside the `hooks.useBeforeClaimPrize` or `hooks.implementation.afterClaimPrize` hook function, thus making the entire prize claim for other winners to be Denied as well(DoS). 

This will make it impossible for the `_claimer` to distribute the prizes among the winners.

## Proof of Concept

```solidity
    for (uint w = 0; w < _winners.length; w++) {
      uint prizeIndicesLength = _prizeIndices[w].length;
      for (uint p = 0; p < prizeIndicesLength; p++) {
        totalPrizes += _claimPrize(
          _winners[w],
          _tier,
          _prizeIndices[w][p],
          _feePerClaim,
          _feeRecipient
        );
      }
    }
```

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

```solidity
      recipient = hooks.implementation.beforeClaimPrize(_winner, _tier, _prizeIndex);
```

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

```solidity
      hooks.implementation.afterClaimPrize(
```

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

## Tools Used
Manual Review and VSCode

## Recommended Mitigation Steps

Hence it is recommended to either omit the `hook` functionality from the `winners` or if `hooks` are an essential part of the protocol then it is recommended to allow the `winner` or a `claimer` to claim the prizes individually rather than iterating through an array of all winners.


## Assessed type

DoS