xanoutas/solidity-security-findings huggingface dataset:
Loss of fees due to multiple issues with `Vault.mintYieldFee()`
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L573
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L394-L399
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L704-L706
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L1229-L1231


# Vulnerability details

## Impact

The function `Vault.mintYieldFee()` is missing access control and can be called by anybody. It also allows to specify any `_recipient` to receive the shares for the yield fee. It ignores the state variable `Vault._yieldFeeRecipient`, which is the address of the yield fee recipient, that can be set via the constructor of the Vault.sol contract or via `Vault.setYieldFeeRecipient()` by the owner of the vault.

All these issues potentially lead to undesired behavior:

1. When `Vault.liquidate()` is called, the function `Vault._increaseYieldFeeBalance` gets called which increases the `_yieldFeeTotalSupply`. After `Vault.liquidate()` was called, a malicious actor can call `Vault.mintYieldFee()` before the liquidator is calling the function, in order to frontrun and steal the shares of the yield fee (`_yieldFeeTotalSupply`), by specifying the malicious actor's address for the `_recipient` function parameter and minting the shares for themselves. `Vault._yieldFeeRecipient` gets ignored.

1. Also the liquidator could send the shares for the yield fee anywhere when calling `Vault.mintYieldFee()`, by specifying any address for the function parameter `_recipient`. `Vault._yieldFeeRecipient` gets ignored.

1. The owner of the Vault.sol contract calls `Vault.setYieldFeeRecipient()` in order to specify who should receive the shares for the yield fee. But when `Vault.mintYieldFee()` is called afterwards, the value `Vault._yieldFeeRecipient` that the owner set is ignored, and the shares for the yield fee are being sent to some other recipient potentially.

Because of these issues, yield fee shares could be lost: either because they could be stolen by a malicious actor or they could be send to an inadvertent recipient by a liquidator. The Vault owner can't influence who will receive the shares for the yield fees.

## Proof of Concept

Here is a POC:

https://gist.github.com/zzzitron/7ec08fc8428ebe86e193dc35717a997f

## Tools Used

Manual review

## Recommended Mitigation Steps

Consider removing the function parameter `_recipient` from `Vault.mintYieldFee()` and send the shares to the specified `Vault._yieldFeeRecipient` of the Vault.sol contract.

Or consider introducing access control to `Vault.mintYieldFee()`:

```solidity
// Vault.sol
394  function mintYieldFee(uint256 _shares, address _recipient) external {
395    if (msg.sender != address(_liquidationPair)) {
396        // revert
```


## Assessed type

Access Control
More shares can be minted than underlying assets available, rendering Vault.sol undercollateralized as a result
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L307-L315
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L818-L819
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L394-L399
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L868
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L886


# Vulnerability details

## Impact

`Vault.availableYieldBalance()` correctly checks, whether the Vault is undercollateralized, and returns 0 if that is the case, since there must not be more shares minted than underlying assets available. It does so by calculating the needed amount of assets for the Vault to be collateralized, based on `_totalShares()`. `_totalShares()` is returning the sum of `_totalSupply()` plus `Vault._yieldFeeTotalSupply`. This is important since `Vault._yieldFeeTotalSupply` can also be converted to shares via `Vault.mintYieldFee()`.

The problem is that the Vault contract also uses another function to determine the vault collateralization: `Vault._requireVaultCollateralized()`, where subsequently `Vault._currentExchangeRate()` is called to do the calculations for the collateralization, but there is a flaw:

- `Vault._currentExchangeRate()` is using `Vault._totalSupply()` to determine the assets needed to be collateralized (Vault.sol line 1169)
- `Vault._totalSupply()` doesn't take into account `Vault._yieldFeeTotalSupply`
- Now when the needed assets are calculated, the calculation uses the result of `Vault._totalSupply()`, which leads to the problem that fewer assets are calculated (`_totalSupplyToAssets`) than needed for the Vault to be collateralized, because `Vault._yieldFeeTotalSupply` wasn't taken into account (Vault.sol line 1170)
- Finally the exchange rate is calculated wrongly as a result of the wrongly calculated `_totalSupplyToAssets` on line 1183, returning a wrong exchange rate which is `1e18`, but should indeed be smaller than `1e18` to indicate that the vault is undercollateralized.
- Then the return value of `Vault._currentExchangeRate()` which was wrongly returned as `1e18` is used to determine whether the vault is collateralized.
- After `Vault._requireVaultCollateralized()` is checked and doesn't revert, more shares can be minted despite the fact that potentially any additional share minted is not covered by the available assets, so minting means to render the Vault undercollateralized as a result.

As a consequence, more shares can be minted than there are underlying assets available, which should not be allowed and which should be reverted - see comment in Vault.sol line 308.

Also as another issue, `Vault._currentExchangeRate()` can potentially and wrongly return `1e18` (or 10**underlyingAssetDecimals i
Direct deposits to the Prize Pool reserves are not accounted, leading to lose of the deposits
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The prize tokens deposited directly to the prize pool can be contributed to the prize pool by anyone. This frees liquidators from providing prize tokens, while they still receive vault shares.
Another impact is the reserves of the prize pool become inflated: the actual amounts of tokens in reserves will be smaller. This can break claiming of prizes when there's not enough tokens in the pool and the reserves are used to pay the prize.
## Proof of Concept
The [PrizePool.increaseReserve](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L498) function allows anyone to deposit prize tokens directly to the reserves of the prize pool. However, these tokens are not accounted in the balance of the prize pool: the [_accountBalance](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L743) function doesn't count the reserves. As a result, the tokens deposited directly to the reserves can also be contributed to the prize pool: the [contributePrizeTokens](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L311) function computes the amount of tokens to contribute as the difference between the current contract's balance and the amount of accounted balances:
```solidity
uint256 _deltaBalance = prizeToken.balanceOf(address(this)) - _accountedBalance();
if (_deltaBalance < _amount) {
    revert ContributionGTDeltaBalance(_amount, _deltaBalance);
}
```

What this means is that, after the `PrizePool.increaseReserve` function is called and some amount of tokens is deposited, the [Vault.liquidate](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L550) function can be called by anyone to contribute the tokens, basically making the liquidation free for the caller. The user who deposited the tokens to the reserve loses them.
## Tools Used
Manual review
## Recommended Mitigation Steps
There doesn't seem to be a quick solution. It's clear that the reserves shouldn't be accounted because they're mainly made from the contributed tokens (after shrinking unused tiers). Thus, it seems that donated reserves should be counted in a separate state variable and the state variable should be accounted in `_accountedBalance`.


## Assessed type

Other
Front-running of liquidations can cause stealing of prize tokens
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L570
https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L312


# Vulnerability details

## Impact
A liquidation of yield can be front run to steal the prize tokens deposited by the victim and to contribute them to the prize pool on behalf of the attacker. The attacker will receive vault shares in exchange for victim's prize tokens; the victim will receive nothing and will lose their prize tokens.
## Proof of Concept
When liquidating yield via the [Vault.liquidate](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L550) function, the caller is expected to contribute some amount of prize tokens to the prize pool: the [_prizePool.contributePrizeTokens](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L570) function is called to perform the contribution. However, neither the Vault contract nor the PrizePool contract pool the prize tokens from the caller. The [PrizePool.contributePrizeTokens](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L311) function expects that prize tokens have already been deposited to the contract ([PrizePool.sol#L312-L315](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L312-L315)):
>> /// @notice Contributes prize tokens on behalf of the given vault. The tokens should have already been transferred to the prize pool.

Since the `Vault.liquidate` function doesn't pull prize tokens from the caller, the only way for the caller to deposit the tokens is to transfer them to the prize pool contract in a separate transaction, before calling the `liquidate` function. This opens up the opportunity to front run the `liquidate` call: a MEV bot cal intercept the `liquidate` call and replicate it to liquidate yield using the prize tokens deposited by the victim. The front runner will receive vault shares while providing no prize tokens; the victim will lose their prize tokens and won't receive any shares.
## Tools Used
Manual review
## Recommended Mitigation Steps
In the `Vault.liquidate` function, consider pulling prize tokens from the caller (or rather the `_account` address) by using the ERC20's `safeTransferFrom` function and transfer them to the prize pool.


## Assessed type

Token-Transfer
Unfair distribution of tier prizes reduces rewards for subsequent winners
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The first winner who claims a prize in a tier gets more tokens than subsequent claimants, even though the prize is expected to be split equally. Since the order of winners during claiming can be arbitrary specified by the claimer, winners can be deliberately sorted in a way to benefit some of them.
## Proof of Concept
In the prize pool, each tier has a fixed number of prizes: the accumulated liquidity of a tier is split into a fixed number of equal prizes so that multiple winners could win prizes from one tier. However, after the first winner has claimed their prize, the remaining liquidity of the tier, while being reduced after the first claim, is again split into the same amount of prizes. Thus, after the first claim, each prize of a tier becomes smaller, and the subsequent claimants receive less tokens than the first winner. The later you claim your prize, the smaller its size is.

The [PrizePool.claimPrize](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L407) function is used to claim prizes from the prize pool. The prize size is determined in the very beginning of the function, by [loading the info about the tier](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L415-L419)–it's stored in `tierLiquidity.prizeSize`. Looking at [_getTier](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/abstract/TieredLiquidityDistributor.sol#L471), we can see that the prize size is computed in the [_computePrizeSize](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/abstract/TieredLiquidityDistributor.sol#L590) function: the function computes the liquidity of the tier by multiplying the amount of prize tokens per share by the amount of shares of the tier; the liquidity is then divided by `_fractionalPrizeCount`, which is [set earlier to the number of prizes of the tier](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/abstract/TieredLiquidityDistributor.sol#L576).

When a prize is claimed, the amount of liquidity equal to the size of the prize is removed from the tier ([PrizePool.sol#L451](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L451)): the [_consumeLiquidity](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/abstract/TieredLiquidityDistributor.sol#L518C12-L518C29) fun
Delegating to the zero address burns the transferred user's `delegateBalance` forever
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L596-L599
https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L622-L626
https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L633-L637


# Vulnerability details

## Impact
Users cannot re-delegate their tokens after delegating them to the zero address. Such users will forever burn their delegated tokens and won't be allowed to participate in draws, since their `delegateBalance` will always be 0.
## Proof of Concept
The [TwabController.delegate](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L491) function lets users delegate their tokens to any address. One of the option is to delegate to the zero address, in case a user wants to opt out of participating in draws: when delegating to the zero address, the [user's balance of delegated tokens is decreased](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L620) and the [total supply of delegated tokens is also decreased](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L622-L626). The function also allows delegating from the zero address: the [total supply is expected to be increased](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L633-L637). The function is deliberately designed to handle the cases when either "to delegate" or "from delegate" addresses is zero, thus delegating to the zero address seems like a valid use case.

However, the "from delegate" address can never be the zero address: when [taking the current delegate, `_delegateOf` is called](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L649), which [returns the user's address if the delegate address is the zero address](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L596-L599). Thus, after delegating to the zero address, the "from delegate" address will be set to the user's address, leading to the following outcomes:
1. if the user is trying to un-delegate from the zero address and delegate to self, [this line will cause a revert](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L649-L652);
1. if the user is trying to re-delegate to another address, [this line will revert](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabCo
Any user's chances to win can be revoked by anyone
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Anyone can revoke the chances to win for any other user. A malicious actor can use the vulnerability to exclude big depositors from the draws, thus increasing the malicious actor's chances to win prizes.
## Proof of Concept
The [Vault.sponsor](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L480) function allows the caller to:
1. deposit funds into the yield vault ([Vault.sol#L983](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L983)):
    ```solidity
    uint256 _shares = deposit(_assets, _receiver);
    ```
1. and delegate all their funds to the sponsorship address ([Vault.sol#L985-L989](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L985-L989)):
    ```solidity
    if (
        _twabController.delegateOf(address(this), _receiver) != _twabController.SPONSORSHIP_ADDRESS()
    ) {
        _twabController.sponsor(_receiver);
    }
    ```

As per the definition of the sponsorship address ([TwabController.sol#L23-L24](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L23-L24)):
> Allows users to revoke their chances to win by delegating to the sponsorship address.

The vulnerability is that the `sponsor` function can be called on any address, without the address' owner permission. A malicious actor can call the `sponsor` address with the amount of assets set to 0 (or 1 wei) and the receiver address set to the address of a whale (the top holder of shares). `_twabController.sponsor` will be called with the whale's address, and all their shares will be delegated to the sponsorship address, thus completely preventing the whale from participating in draws.

As can be seen from the [_transferDelegateBalance](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L619-L631) function, when delegating to the sponsorship address, the delegated balance of the user and the total supply is decreased. This will [create a new observation with the delegate balance of the whale set to 0](https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/libraries/TwabLib.sol#L171-L210). The TWAB of a user is [the most important metric when determining if the user is a winner in a draw or not](https://github.com/GenerationSoftware/pt-v5-prize-pool/blob/4bc8a12b857856828c018510b5500d722b79ca3a/src/PrizePool.sol#L864-L877). Thus, by delegating user's balance to the sponsorship address, the TWAB of the user will be reduced, thus reducing their chances to win draws. Since the `sponsor` function can be called on any address, it
Unrestricted `Vault.mintYieldFee` function allows stealing of mint yield
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Anyone can claim the yield fees and mint the extra shares to their address. The shares can then be redeemed for assets.
## Proof of Concept
The [Vault.mintYieldFee](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L394) function allow minting the yield fee that was set out during liquidations. The function can be called by anyone and an arbitrary fee recipient address can be provided, allowing anyone to steal accumulated yield fee.

The contract defines the [_yieldFeeRecipient](https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L227) state variable, which is the address that's expected to receive yield fees. However, the variable is not used in the `Vault.mintYieldFee` function.
## Tools Used
Manual review
## Recommended Mitigation Steps
In the `Vault.mintYieldFee` function, consider minting shares only to the address specified in the `_yieldFeeRecipient` state variable.


## Assessed type

Access Control
`deposit` and `mint` functions in `Vault.sol` can be easily front-ran making the user loose funds
Audit: 2023-07-pooltogether-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The `_deposit` function which is used in both `deposit` and `mint` functions requires the user to transfers the asset tokens to the `Vault` contract before calling one of the functions, to protect themselves against reentrancy attacks from ERC777 as stated in the comments https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L934-L941 , which makes the functions very vulnerable to front-run attacks by any user.

## Proof of Concept
Imagine this simple case :
- Alice wants to deposit `asset` tokens into a vault so she could get some shares of the vault to interact and participate in the protocol
- First she `transfer` the amount of tokens that she wants to deposit and calls the `deposit` function so she could get the shares accounted in `TwabController.sol`
- Bob sees the transactions in the mempool and front-runs the `deposit` call, right after she transfers the tokens, with his transaction calling `deposit` setting the receiver as himself.
- The `asset` tokens will get deposited into the `_yieldVault` and Bob will get accounted with all of the shares, making Alice lose all of her assets that she transferred and not getting anything in return.

## Tools Used
Manual review
## Recommended Mitigation Steps
Consider using `transferFrom` even if there is a risk of reentrancy with ERC777, it is better using other type of protections against reentrancy than making the user transfer the funds before since this can be very easily abused by any attacker.


## Assessed type

MEV