xanoutas/solidity-security-findings huggingface dataset:
PrizePool -> Winners wouldn't be able to claim prize correctly in `claimPrize` function 
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In `PrizePool.sol` vaults can call `claimPrize()` function to claim prizes for winners. The `claimPrize` calls `_getTier(_tier, numberOfTiers)` function to get back the **prizeSize** but there is a problem in [`_getTier`](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/abstract/TieredLiquidityDistributor.sol#L471) function, lets see:
```solidity
  function _getTier(uint8 _tier, uint8 _numberOfTiers) internal view returns (Tier memory) {
    Tier memory tier = _tiers[_tier];
    uint16 _lastClosedDrawId = lastClosedDrawId;
    if (tier.drawId != _lastClosedDrawId) {
      tier.drawId = _lastClosedDrawId;
      tier.prizeSize = uint96(
        _computePrizeSize(
          _tier,
          _numberOfTiers,
          fromUD34x4toUD60x18(tier.prizeTokenPerShare),
          fromUD34x4toUD60x18(prizeTokenPerShare)
        )
      );
    }
    return tier;
  }
```
As you can see it calls `_computePrizeSize` function and downcast the returned result but since it returns `uint256` by downcasting it to `uint96` it's possible to result in incorrect number if the value overflow (When downcasting from one type to another, Solidity will not revert but overflows).
So this means there is possibilty `prizeSize` value result in incorrect prize size and vaults can't claim the prizes correctly for winners.


## Tools Used

Manual Review

## Recommended Mitigation Steps

Check whether the result of `prizeSize` exceeds `type(uint96).max` or not, if so revert the tx.
I recommend to use [SafeCast](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) by OpenZeppelin for casting diffrent types safely.

I also found some other downcastings in the code base but since they or pegged to vault share values and you already check for overflows, there is no problem.


## Assessed type

Under/Overflow
Uninitialized local variable may cause unintended behaviour  
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The current implementation of the _claimPrize function in Vault.sol does not include proper validation for the recipient address returned by the beforeClaimPrize hook. As a result, if the hook does not effectively return a valid address, the recipient variable will be left uninitialized, defaulting to the zero address (address(0)). This may cause unintended behaviors. For example, this could lead to the prize being transferred to the zero address if the prize token contract does not handle such a case properly.

[Vault.sol#L1051](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1051)
```solidity
function _claimPrize(
    address _winner,
    uint8 _tier,
    uint32 _prizeIndex,
    uint96 _fee,
    address _feeRecipient
  ) internal returns (uint256) {
    VaultHooks memory hooks = _hooks[_winner];
    address recipient;

    if (hooks.useBeforeClaimPrize) {
      recipient = hooks.implementation.beforeClaimPrize(_winner, _tier, _prizeIndex);

    } else {
      recipient = _winner;
    }
    //Rest of the code
    //...
}
```

## Proof of Concept
Consider this faulty hook implementation from a winner:
```solidity
contract FaultyHook {

  constructor() {}

  function beforeClaimPrize(
    address winner,
    uint8 tier,
    uint32 prizeIndex
  ) external returns (address) {
    bool index = false;
    if (index) {
      return address(this);
    }
  }

  //Rest of code
  //...
}
```
The beforeClaimPrize function does not return anything. As a consequence, the provided test case would pass, leading to a transfer of the prize to the zero address.

```diff
diff --git a/test/unit/Vault/Vault.t.sol b/test/unit/Vault/Vault.t.sol
index d0d6d30..7cedec6 100644
--- a/test/unit/Vault/Vault.t.sol
+++ b/test/unit/Vault/Vault.t.sol
@@ -4,6 +4,7 @@ pragma solidity 0.8.17;
 import { UnitBaseSetup, LiquidationPair, PrizePool, TwabController, VaultMock, ERC20, IERC20, IERC4626 } from "test/utils/UnitBaseSetup.t.sol";
 import { IVaultHooks, VaultHooks } from "../../../src/interfaces/IVaultHooks.sol";
 import "src/Vault.sol";
+import { FaultyHook } from "./FaultyHook.sol";
 
 contract VaultTest is UnitBaseSetup {
   /* ============ Events ============ */
@@ -174,6 +175,33 @@ contract VaultTest is UnitBaseSetup {
     vm.stopPrank();
   }
 
+  //@audit - This test should pass as the recipient address is set to 0 when claiming prize.
+  function testClaimPrize_faultyHook() public {
+    FaultyHook faultyHook = new FaultyHook();
+    vm.startPrank(alice);
+    VaultHooks memory hooks = VaultHooks({
+      useBeforeClaimPrize: true,
+      useAfterClaimPrize: false,
+      implementation: IVaultHooks(address(faultyHook))
+    });
+    vault.setHooks(hooks);
+    vm.stopPrank();
+
+    vm.startPrank(address(claimer));
+
+    mockPrizePoolClai
Vault will lose its `_yieldFeeTotalSupply` without getting an equivalent amount of share tokens
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In `Vault.sol`/`mintYieldFee` function:

- the vault shares are minted in return of yield fees.
- the `_yieldFeeTotalSupply` is updated by decreasing the `_shares` amount (knowing that `_yieldFeeTotalSupply` & `_shares` are of `uint256` type):

```solidity
 _yieldFeeTotalSupply -= _shares;
```

- but in `_mint` function: it will only mint `uint96(_shares)`.

```solidity
_twabController.mint(_receiver, uint96(_shares));
```

- this will reduce the `_yieldFeeTotalSupply` without getting an equivalent share tokens if `_shares` is greater than `type(uint96).max`.
**Impact**:
- The vault will lose from its `_yieldFeeTotalSupply` without getting an equivalent amount of share tokens `_shares`.
- Total amount of shares managed by the vault will be less than actual amount,

## Proof of Concept

[Vault.sol/Line 398](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L398)

```solidity
File:pt-v5-vault/src/Vault.sol
Line 398: _yieldFeeTotalSupply -= _shares;
```

[Vault.sol/Line 1123](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1123)

```solidity
File:pt-v5-vault/src/Vault.sol
Line 1123: _twabController.mint(_receiver, uint96(_shares));
```

## Tools Used

Manual Testing.

## Recommended Mitigation Steps

In `mintYieldFee` function : deduct the `uint96(_shares)` amount from `_yieldFeeTotalSupply`:

```solidity
_yieldFeeTotalSupply -= uint96(_shares);
```


## Assessed type

Math
PRBMATH `pow()` function can return inconsistent values which is root of computation for the  Area under curve`c(d) = −t ∗ ln(α)∗α ^ d *  `
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/PaulRBerg/prb-math/blob/df27d3d12ce12153fb166e1e310c8351210dc7ba/src/sd59x18/Math.sol#L596-L609
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/libraries/DrawAccumulatorLib.sol#L390-L399


# Vulnerability details

## Impact
Protocol is highly dependent on the computation of area under the curve `c(d) = −t ∗ ln(α)∗α ^ d ` which uses PRB-Math's Pow() function to calculate . Recently It has been found this `pow() ` function can give inconsistent values and break the computation.

PRBMath's `pow()` function takes two signed integer (say a and b) and returns a**b
A proper implementation of power function states that :
for any a >= b and x >= 0  , a ** x >= b ** x 

Simplifying this further as (a>=b)= ((a/b)>=1) and so (a/b)**x >=1 [x>=0]
this `pow()` function doesn't follow this  and gives inconsistent values.

This Mainly affects `integrate` function of `DrawAccumulatorLib.sol` which integrate from a lower value higher value for the exponential weighted average

## Proof of Concept
```solidity
File: prb-math/src/sd59x18/Math.sol

596: function pow(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    if (xInt == 0) {
        result = yInt == 0 ? UNIT : ZERO;
    } else {
        if (yInt == uUNIT) {
            result = x;
        } else {
            result = exp2(mul(log2(x), y));
        }
    }
609: }

```

Test Function 
```solidity
function testPow() public {
uint128 high = 505456470057136353;
uint128 low = 505456461792312955;
assert(high > low);
SD59x18 exp = UD2x18.wrap(31414735).intoSD59x18();
SD59x18 highPow = high.intoSD59x18().pow(exp);
SD59x18 lowPow = low.intoSD59x18().pow(exp);
assert(SD59x18.unwrap(highPow) < SD59x18.unwrap(lowPow));
}

```
Root cause described by a senior watson - "in the exp2() function, bit masks are applied to multiply the result by root(2,2^-i) for each position i with a value of 1.
However at Common.sol#L170, the value is incorrectly included as 0xFF00000000 instead of 0xFF000000 "

## Tools Used
Manual , Foundry Testing

## Recommended Mitigation Steps

Ensure that calculation is accurate by adjusting the comparison value in `exp2()` function


## Assessed type

Math
`checkedSub()` might revert due to underflow
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/generationsoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/libraries/OverflowSafeComparatorLib.sol#L55


# Vulnerability details

## Impact
`checkedSub()` might revert due to underflow and several internal functions calling [_calculateTemporaryObservation()](https://github.com/generationsoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/libraries/TwabLib.sol#L330) wouldn't work.

## Proof of Concept
`checkedSub()` calculates the difference between two timestamps.

```solidity
  function checkedSub(uint32 _a, uint32 _b, uint32 _timestamp) internal pure returns (uint32) {
    // No need to adjust if there hasn't been an overflow

    if (_a <= _timestamp && _b <= _timestamp) return _a - _b;

    uint256 aAdjusted = _a > _timestamp ? _a : _a + 2 ** 32;
    uint256 bAdjusted = _b > _timestamp ? _b : _b + 2 ** 32;

    return uint32(aAdjusted - bAdjusted);
  }
```

During calculation, it's possible to meet the underflow but it doesn't care at all.

It's from the [Uniswap V3 Oracle](https://github.com/Uniswap/v3-core/blob/3e88af408132fc957e3e406f65a0ce2b1ca06c3d/contracts/libraries/Oracle.sol#L2C1-L2C25) but there is no problem because it uses the lower version than 0.8.0.

As a result, `_extrapolateFromBalance()` and several internal functions might revert.

```solidity
  function _extrapolateFromBalance(
    ObservationLib.Observation memory _observation,
    uint32 _timestamp
  ) private pure returns (uint128 cumulativeBalance) {
    // new cumulative balance = provided cumulative balance (or zero) + (current balance * elapsed seconds)
    return
      _observation.cumulativeBalance +
      uint128(_observation.balance) *
      (_timestamp.checkedSub(_observation.timestamp, _timestamp)); //@audit revert
  }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
All calculations in `checkedSub` should be in `unchecked` block to work the same as Uniswap.


## Assessed type

Under/Overflow
`Vault.liquidate()` will revert when `_yieldFeePercentage` is 100%
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/tree/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L574
https://github.com/GenerationSoftware/pt-v5-vault/tree/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1218-L1224


# Vulnerability details

## Impact

When the yield fee percentage is 100%, the `liquidate()` method will not work.


## Proof of Concept

In `Vault.liquidate()`, we get fee portion from share amount out and increase yield fee balance with it.

```solidity
    (_amountOut * FEE_PRECISION) / (FEE_PRECISION - _yieldFeePercentage) - _amountOut
```

If `_yieldFeePercentage` is `FEE_PRECISION`, i.e. 100%, `liquidate()` will revert. And the edge case passes the validation in the set method.

```solidity
  function _setYieldFeePercentage(uint256 yieldFeePercentage_) internal {
    if (yieldFeePercentage_ > FEE_PRECISION) {
      revert YieldFeePercentageGTPrecision(yieldFeePercentage_, FEE_PRECISION);
    }
    _yieldFeePercentage = yieldFeePercentage_;
  }

```

As a result, `liquidate()` will not work in the edge case when the fee percentage is 100%.



## Tools Used
Manual Review

## Recommended Mitigation Steps
We can input the original fee amount as a parameter instead of getting from `_amountOut`


## Assessed type

Error
`Vault.mintWithPermit()` can be DOSed 
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/tree/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L466-L469
https://github.com/GenerationSoftware/pt-v5-vault/tree/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L971-L974
https://github.com/GenerationSoftware/pt-v5-vault/tree/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L882-L887


# Vulnerability details

## Impact
`Vault.mintWithPermit()` uses a signature to approve the underlying asset. But the asset amount can be changed easily, so this method can be reverted and might be DoSed.

## Proof of Concept
`Vault.mintWithPermit()` gets the share amount as an input and calculates the asset amount from the share. And then approves the asset amount with `permit` method. 

```solidity
    uint256 _assets = _beforeMint(_shares, _receiver);

    _permit(IERC20Permit(asset()), msg.sender, address(this), _assets, _deadline, _v, _r, _s);
    _deposit(msg.sender, _receiver, _assets, _shares);
```

The signature is generated using the exact value of the expected asset amount calculated from the share amount, and the resulting asset amount depends on the exchange rate of current vault.

```solidity
  function _beforeMint(uint256 _shares, address _receiver) internal view returns (uint256) {
    if (_shares > maxMint(_receiver)) revert MintMoreThanMax(_receiver, _shares, maxMint(_receiver));
    return _convertToAssets(_shares, Math.Rounding.Up);
  }
```
```solidity
  function _convertToAssets(
    uint256 _shares,
    Math.Rounding _rounding
  ) internal view virtual override returns (uint256) {
    return _convertToAssets(_shares, _currentExchangeRate(), _rounding);
  }
```

So the resulting asset amount can be different from the value of transaction start time. And even an adversary can front-run and manipulate the exchange rate. If the resulting asset amount is different from the original one, the signature will not work as expected and `mintWithPermit()` will revert in most cases.

## Tools Used
Manual Review

## Recommended Mitigation Steps
We can input upper bound of the asset amount instead of the exact value of the asset amount.


## Assessed type

DoS
Users can grief high prize winner by taking advantage of VRGDA mechanism
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-claimer/blob/57a381aef690a27c9198f4340747155a71cae753/src/libraries/LinearVRGDALib.sol#L39


# Vulnerability details

## Impact
A malicious actor can grief a low-tier prize winner by taking advantage of the VRGDA system to make the period at which fee prices are reasonable extremely short. The gas price for this short period of time will dictate if the prize gets claimed.
## Proof of Concept
The fees paid for claiming prizes to bots are calculated based on a variable rate gradual reverse Dutch auction where they start at 0 at time 0 and are incremented until the target price at the target time. 
This mechanism gradually increases the fee price it's willing to offer to bots for executing the prize claims. The target time is the draw period and the target price is the maxFee that the protocol is willing to pay per claim. 

It compares the amount sold to the expected amount sold at the current time, and the result will dictate how sharply it increases the price.
If too many are being sold relative to what's expected, the following price calculations will result in a really low price, which won't increase much until the current time catches up with the expected time at which the number of units that were sold was supposed to be sold.

**A bot could claim all the prizes except the high tier 0 prize.**
In an example with 10.000 prizes to claim, if a bot were to buy 9.999 fees at the beginning of the auction, this would leave a really low price until the current time is actually close to the expected time at which 9.999 fees should be sold, meaning at time 9.999/10.000 of the draw period.

This means that only at the last moment would prices start scaling up, which leaves bots with a small margin of time at which they can buy fees at a reasonable price. This puts extra pressure on the gas price at that time, which can cause bots to skip the claiming of that prize since the gas cost of claiming that prize is no longer profitable.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Setting a shorter time period for the VRGDA, and leaving a time period where maxFee has been reached to counter the pressure.


## Assessed type

Timing
Denial-of-service attack on "prizes claiming" transaction
Audit: 2023-07-pooltogether-findings
Severity: MEDIUM

# Lines of code

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

The "prizes claiming" transaction can be under a denial-of-service (DoS) attack.

## Proof of Concept

The `Vault._claimPrize()` implements hook triggers ([beforeClaimPrize](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1053) and [afterClaimPrize](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1068)) and calls them if a winner enables one of them. 

In this way, an attacker can register hook functions that can make a denial-of-service (DoS) attack on the "prizes claiming" transaction by reverting the transaction, creating a returnbomb attack, or spending all available gas.

```solidity
	function _claimPrize(
		address _winner,
		uint8 _tier,
		uint32 _prizeIndex,
		uint96 _fee,
		address _feeRecipient
	) internal returns (uint256) {
		VaultHooks memory hooks = _hooks[_winner];
		address recipient;
		if (hooks.useBeforeClaimPrize) {
@>		recipient = hooks.implementation.beforeClaimPrize(_winner, _tier, _prizeIndex);
		} else {
			recipient = _winner;
		}

		uint prizeTotal = _prizePool.claimPrize(
			_winner,
			_tier,
			_prizeIndex,
			recipient,
			_fee,
			_feeRecipient
		);

		if (hooks.useAfterClaimPrize) {
@>		hooks.implementation.afterClaimPrize(
				_winner,
				_tier,
				_prizeIndex,
				prizeTotal - _fee,
				recipient
			);
		}

		return prizeTotal;
	}
```

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

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

## Impact

Since one "prizes claiming" transaction can contain a batch of multiple winners, other legit winners will not be able to receive their prizes.

Of course, the likelihood of this issue might be considered "LOW" (since the attacker must be one of the winners), but the impact of this issue is considered "HIGH".

## Tools Used

Manual Review

## Recommended Mitigation Steps

I recommend applying the [excessivelySafeCall()](https://github.com/nomad-xyz/ExcessivelySafeCall/blob/81cd99ce3e69117d665d7601c330ea03b97acce0/src/ExcessivelySafeCall.sol#L24) to avoid the DoS attack via the `beforeClaimPrize` and `afterClaimPrize` hook functions.


## Assessed type

DoS
disrupting program flow and potential loss of data or funds in setDrawManager function
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


# Vulnerability details



## [M-01]  disrupting program flow and potential loss of data or funds in setDrawManager function

The setDrawManager function sets the drawManager address to the input parameter _drawManager and emits an event DrawManagerSet. However, before setting the drawManager, it checks if it is not already set by verifying if it is not equal to address(0).

The risk here is that if the drawManager is already set to a non-zero address, the function will revert with an error message DrawManagerAlreadySet(). This can cause disruption in the normal flow of the program and may cause loss of data or funds if the function is called at an inappropriate time or in an inappropriate context

## The impact 
 the setDrawManager function can be significant. If the function is called at an inappropriate time or in an inappropriate context, it can cause disruption in the normal flow of the program and may result in loss of data or funds.

For example, if the function is called after the drawManager has already been set, it will revert with an error message and prevent any further changes to the drawManager. This can cause delays in the execution of other functions that rely on the drawManager and may lead to unexpected behavior or errors.

In addition, if the input parameter _drawManager is not validated properly, it can result in unintended consequences such as assigning the wrong address to the drawManager, which can cause further issues down the line.

Overall, the impact of this risk can be significant and should be mitigated through proper checks, validations, and authorization mechanisms.


#### Proof of concept 

the setDrawManager function can be demonstrated by creating a test scenario where the function is called multiple times with different input parameters.

For example, we can create a test contract that calls the setDrawManager function with a non-zero address, then calls it again with a different non-zero address. We can then verify that the drawManager is set to the second address and that the event DrawManagerSet is emitted.

Next, we can call the function again with the first non-zero address and verify that it reverts with the error message DrawManagerAlreadySet(). This will demonstrate that the function can cause disruption in the normal flow of the program and may prevent further changes to the drawManager.

Finally, we can test the function with an input parameter that is not a valid address, such as a string or an integer, and verify that it reverts with an error message or causes unintended consequences. This will demonstrate that proper input validation is necessary to prevent unintended consequences and potential loss of data or funds.

Overall, this proof of concept will demonstrate the potential risks and consequences of the setDrawManager function and highlight the impo