xanoutas/solidity-security-findings huggingface dataset:
RemoteOwner circular dependency at deployment time
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/remote-owner/blob/9c093dbd36c1f18ab7083549d10ac601d91630df/src/RemoteOwner.sol#L58
https://github.com/GenerationSoftware/remote-owner/blob/9c093dbd36c1f18ab7083549d10ac601d91630df/src/RemoteOwner.sol#L120
https://github.com/GenerationSoftware/remote-owner/blob/9c093dbd36c1f18ab7083549d10ac601d91630df/src/RemoteOwner.sol#L96-L99
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuctionRelayerRemoteOwner.sol#L47
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuctionRelayerRemoteOwner.sol#L64


# Vulnerability details

## Impact
The `RemoteOwner.sol` contract has a security measure that ensures the sender from the remote/origin chain was the origin chain owner (i.e. a `RngAuctionRelayerRemoteOwner.sol` deployment), and this address is set at deployment time in the constructor. The `RngAuctionRelayerRemoteOwner` contract also has a security measure to ensure that messages are only dispatched across chain to the `RemoteOwner` contract deployed in the destination chain, and this address is set at deployment time in the constructor.

Clearly there is a circular dependency here that means the deployment phase will fail. There is a `setOriginChainOwner` method on the `RemoteOwner` contract, however this can only be called by the address on the origin chain specified in the constructor. This method is never called from the origin chain either. In summary, the circular dependency prevents the contracts from being deployed and ever initialised properly.

It is possible that there is an intermediary `__originChainOwner` used in the constructor when deploying `RemoteOwner`, but since I couldn't find any deployment scripts to verify this I have assumed that this is an unintended bug. The severity of this report depends on whether or not this was intended.

## Proof of Concept
In the `RemoteOwner.sol` contract, the origin chain owner is set in the constructor:

```
  constructor(
    uint256 originChainId_,
    address executor_,
    address __originChainOwner
  ) ExecutorAware(executor_) {
    if (originChainId_ == 0) revert OriginChainIdZero();
    _originChainId = originChainId_;
    _setOriginChainOwner(__originChainOwner);
  }
```

Any calls to the `RemoteOwner` contract are protected by the `_checkSender` view:

```
  function _checkSender() internal view {
    if (!isTrustedExecutor(msg.sender)) revert LocalSenderNotExecutor(msg.sender);
    if (_fromChainId() != _originChainId) revert OriginChainIdUnsupported(_fromChainId());
    if (_msgSender() != address(_originChainOwner)) revert OriginSenderNotOwner(_msgSender());
  }
```

Now, if we have a look at the `RngAuctionRelayerRemoteOwner.sol` contract, we can see that the remote owner address is also specified in the constructor:

```
    constructor(
        RngAuction _rngAuction,
        ISingleMessageDispatcher _messageDispatc
PRBMATH `SD59x18.exp()` reverts on hugely negative numbers.
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/libraries/ContinuousGDA.sol#L34-L36
https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/libraries/ContinuousGDA.sol#L64
https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/libraries/ContinuousGDA.sol#L85-L87
https://github.com/PaulRBerg/prb-math/blob/5959ef59f906d689c2472ed08797872a1cc00644/src/sd59x18/Math.sol#L168-L181


# Vulnerability details

## Impact
`ContinuousGDA.sol` inherits a version of `PRB Math` that contains a vulnerability in the `SD59x18.exp()` function, which can be reverted on hugely negative numbers. `SD59x18.exp()` is used for calculations in `ContinuousGDA.sol#purchasePrice()` , `ContinuousGDA.sol#purchaseAmount()` and `ContinuousGDA.sol#computeK()`. Recently, the creators of the `PRBMath` have acknowledged this situation. Here is the corresponding [link](https://github.com/PaulRBerg/prb-math/issues/200). This issue should be proactively corrected by `PoolTogether` to avoid unexpected results that corrupt the protocol's computation flow.
## Proof of Concept
There are 05 instances of this issue: [see here](https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/libraries/ContinuousGDA.sol#L34-L36)
```
File: ContinuousGDA.sol
34:    topE = topE.exp().sub(ONE);
36:    bottomE = bottomE.exp();
64:    SD59x18 exp = _decayConstant.mul(_timeSinceLastAuctionStart).exp();
85:    SD59x18 eValue = exponent.exp();
87:    SD59x18 denominator = (_decayConstant.mul(_purchaseAmount).div(_emissionRate)).exp().sub(ONE);
```
[Proof of the bug acknowledgment by the creator of the PRBMath](https://github.com/PaulRBerg/prb-math/issues/200)
`SD59x18.exp()` correctly returns 0 for inputs less than (roughly) -41.45e18, however it starts to throw `PRBMath_SD59x18_Exp2_InputTooBig` when the input gets hugely negative. This is because of the unchecked multiplication in `exp()` overflowing into positive values: [see here](https://github.com/PaulRBerg/prb-math/blob/5959ef59f906d689c2472ed08797872a1cc00644/src/sd59x18/Math.sol#L168-L181)
```
function exp(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xInt > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_SD59x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        int256 doubleUnitProduct = xInt * uLOG2_E;                // <== overflow
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}
```
## Tools Used
Manual Review and [Proof of the bug acknowledgment by the creator of the PRBMath](https://github.com/PaulRBerg/prb-math/issues/200)
## Recommended Mitigation Steps
A potential fix would be to compare the input with the smalles
It is possible that function `rngComplete()` does not iterate through all rewards
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L157-L173


# Vulnerability details

## Impact

In `RngRelayAuction.sol` we have `rngComplete()`:
```solidity
function rngComplete(
    uint256 _randomNumber,
    uint256 _rngCompletedAt,
    address _rewardRecipient,
    uint32 _sequenceId,
    AuctionResult calldata _rngAuctionResult
  ) external returns (bytes32) {
    if (_sequenceHasCompleted(_sequenceId)) revert SequenceAlreadyCompleted();
    uint64 _auctionElapsedSeconds = uint64(block.timestamp < _rngCompletedAt ? 0 : block.timestamp - _rngCompletedAt);
    if (_auctionElapsedSeconds > (_auctionDurationSeconds-1)) revert AuctionExpired();
    // Calculate the reward fraction and set the draw auction results
    UD2x18 rewardFraction = _fractionalReward(_auctionElapsedSeconds);
    _auctionResults.rewardFraction = rewardFraction;
    _auctionResults.recipient = _rewardRecipient;
    _lastSequenceId = _sequenceId;

    AuctionResult[] memory auctionResults = new AuctionResult[](2);
    auctionResults[0] = _rngAuctionResult;
    auctionResults[1] = AuctionResult({
      rewardFraction: rewardFraction,
      recipient: _rewardRecipient
    });

    uint32 drawId = prizePool.closeDraw(_randomNumber);

    uint256 futureReserve = prizePool.reserve() + prizePool.reserveForOpenDraw();
    uint256[] memory _rewards = RewardLib.rewards(auctionResults, futureReserve);

    emit RngSequenceCompleted(
      _sequenceId,
      drawId,
      _rewardRecipient,
      _auctionElapsedSeconds,
      rewardFraction
    );

    for (uint8 i = 0; i < _rewards.length; i++) {
      uint104 _reward = uint104(_rewards[i]);
      if (_reward > 0) {
        prizePool.withdrawReserve(auctionResults[i].recipient, _reward);
        emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward);
      }
    }

    return bytes32(uint(drawId));
  }
```

This function handles the completion of an RNG relay auction. The problem is this block of code:

```solidity
for (uint8 i = 0; i < _rewards.length; i++) {
      uint104 _reward = uint104(_rewards[i]);
      if (_reward > 0) {
        prizePool.withdrawReserve(auctionResults[i].recipient, _reward);
        emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward);
      }
    }
```

The variable `i` is declared as a `uint8`, which means it can only hold values from 0 to 255. If `_rewards.length` were to be more than 255, the function will revert.
## Proof of Concept

The issue lies in the use of `uint8` as the loop iterator, which can only represent values between 0 to 255. If the length of the `_rewards` array exceeds 255, the loop will revert.

In `rngComplete()` we see the `_rewards` is uint256:

```soldiity
157: uint256[] memory _rewards = RewardLib.rewards(auctionResults, futureReserve);
```
After this in the for loop `i` is `uint8`:

```solidity
for (uint8 i = 0; i < _rewards.le
Closing the draw will be compromised if the recipient reward is `address(0)`
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L170
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuction.sol#L170
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuctionRelayerDirect.sol#L36
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuctionRelayerRemoteOwner.sol#L59


# Vulnerability details

## Impact

The [rngComplete()](https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L131C12-L131C23) function receives the `RNG request results`, it closes the draw [using the randomNumber](https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L154) generated by the RNG request auction and it [transfer the rewards](https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L170).

The problem is that a malicious actor can introduce a address zero in the recipient reward causing the revert of the `rngComplete()` function, this will cause a destabilization because:
- [Draws will not be closed](https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngRelayAuction.sol#L154) causing problems for the winners to claim prizes
- The protocol will be unable to close draws and start the next one.

## Proof of Concept

In the [RNG request auction](https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuction.sol#L170) there is not any restriction about to use `address(0)` in the `_rewardRecipient` parameter.

```solidity
  function startRngRequest(address _rewardRecipient) external {
    if (!_canStartNextSequence()) revert CannotStartNextSequence();

    RNGInterface rng = _nextRng;

    uint64 _auctionElapsedTimeSeconds = _auctionElapsedTime();
    if (_auctionElapsedTimeSeconds > auctionDuration) revert AuctionExpired();

    (address _feeToken, uint256 _requestFee) = rng.getRequestFee();
    if (_feeToken != address(0) && _requestFee > 0) {
      if (IERC20(_feeToken).balanceOf(address(this)) < _requestFee) {
        // Transfer tokens from caller to this contract before continuing
        IERC20(_feeToken).transferFrom(msg.sender, address(this), _requestFee);
      }
      // Increase allowance for the RNG service to take the request fee
      IERC20(_feeToken).safeIncreaseAllowance(address(rng), _requestFee);
    }

    (uint32 rngRequestId,) = rng.requestRandomNumber();
    uint32 sequenceId = _openSequenceId();
    UD2x18 rewardFraction = _currentFractionalReward();

    _lastAuction = RngAuctionResult({
      recipient: _rewardRecipient,
      rewardFractio
[M-02] Denial of Service on failed call Dos
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuctionRelayerDirect.sol#L39


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
External calls can fail accidentally or deliberately, which can cause a DoS condition in the contract. To minimize the damage caused by such failures, it is better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically (this also reduces the chance of problems with the gas limit).

## Proof of Concept
Provide direct links to all referenced code in GitHub. 
```sol
https://github.com/GenerationSoftware/pt-v5-draw-auction/blob/f1c6d14a1772d6609de1870f8713fb79977d51c1/src/RngAuctionRelayerDirect.sol#L39
        (bool success, bytes memory returnData) = address(_rngAuctionRelayListener).call(data);
```
Add screenshots, logs, or any other relevant proof that illustrates the concept.
```sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "/2023-08-pooltogether/pt-v5-draw-auction/src/RngAuctionRelayerDirect.sol";

import  "/2023-08-pooltogether/pt-v5-draw-auction/src/interfaces/IRngAuctionRelayListener.sol";

contract AttackRngAuctionRelayerDirect {

    RngAuctionRelayerDirect public rngAuctionRelayListener;

    constructor(RngAuctionRelayerDirect _rngAuctionRelayListener) public payable {
        rngAuctionRelayListener = RngAuctionRelayerDirect(_rngAuctionRelayListener);
    }


    function attack(RngAuctionRelayerDirect _rngAuctionRelayListener) public payable {
        rngAuctionRelayListener.relay(IRngAuctionRelayListener(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4), msg.sender);
        }

}
```
NB: Victim address in this test case is 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
Test Case
1. Deploy contract named AttackRngAuctionRelayerDirect.sol at victim address named RngAuctionRelayerDirect.sol.
2. Go to attack button and enter victim contract address.
3. Click attack button.
4. Dos occurs.

## Tools Used
VS Code

## Recommended Mitigation Steps
Do not use call function.
use require function on call.


## Assessed type

DoS
Missing `deadline` param in `swapExactAmountOut()` allowing outdated slippage and allow pending transaction to be executed unexpectedly.
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationRouter.sol#L63-L80
https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationPair.sol#L211-L226


# Vulnerability details


## Impact

Loss of funds/tokens for the protocol, since block execution is delegated to the block validator without a hard deadline.

## Proof of Concept

The function `swapExactAmountOut()` from `LiquidationRouter.sol` and `LiquidationPair.sol` use these methods to swap tokens:

`source.liquidate(_account, tokenIn, swapAmountIn, tokenOut, _amountOut);`

and

`_liquidationPair.swapExactAmountOut(_receiver, _amountOut, _amountInMax);`

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationPair.sol#L211-L226

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationRouter.sol#L63-L80

Both methods make sure to pass slippage (minimum amount out), but miss to provide the deadline which is crucial to avoid unexpected trades/losses for users and protocol.

Without a deadline, the transaction might be left hanging in the mempool and be executed way later than the user wanted.

That could lead to users/protocol getting a worse price, because a validator can just hold onto the transaction. And when it does get around to putting the transaction in a block

One part of this change is that PoS block proposers know ahead of time if they're going to propose the next block. The validators and the entire network know who's up to bat for the current block and the next one.

This means the block proposers are known for at least 6 minutes and 24 seconds and at most 12 minutes and 48 seconds.

Further reading:
https://blog.bytes032.xyz/p/why-you-should-stop-using-block-timestamp-as-deadline-in-swaps

## Tools Used
Manual

## Recommended Mitigation Steps

Let users provide a fixed deadline as param, and also never set deadline to `block.timestamp`.



## Assessed type

Other
Potential Near-Zero Scenarios for purchasePrice in the Continuous Gradual Dutch Auction
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationPair.sol#L211-L226
https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationPair.sol#L294-L319
https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/libraries/ContinuousGDA.sol#L16-L44


# Vulnerability details

## Impact
The Continuous Gradual Dutch Auction (CGDA) model has potential scenarios where the `purchasePrice` for an amount of tokens could approach near-zero values. This is influenced mainly by two factors: `_emissionRate` and `_timeSinceLastAuctionStart`. If either one or both of these factors (`_emissionRate` specifically more likely) are significantly large, the `purchasePrice` could drastically drop.

This condition could cause undesired economic effects in the auction process. Under this context, participants may acquire tokens (amount of Vault shares) at an extremely low price (very low POOL amount indeed), which could lead to significant chance of winnings.

## Proof of Concept
Here is the purchasePrice function within the ContinuousGDA library, which computes the purchase price of tokens based on various parameters.

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/libraries/ContinuousGDA.sol#L23-L44

```solidity
  function purchasePrice(
    SD59x18 _amount,
    SD59x18 _emissionRate,
    SD59x18 _k,
    SD59x18 _decayConstant,
    SD59x18 _timeSinceLastAuctionStart
  ) internal pure returns (SD59x18) {
    if (_amount.unwrap() == 0) {
      return SD59x18.wrap(0);
    }
    SD59x18 topE = _decayConstant.mul(_amount).div(_emissionRate);
    topE = topE.exp().sub(ONE);
    SD59x18 bottomE = _decayConstant.mul(_timeSinceLastAuctionStart);
    bottomE = bottomE.exp();
    SD59x18 result;
    if (_emissionRate.unwrap() > 1e18) {
      result = _k.div(_emissionRate).mul(topE).div(bottomE);
    } else {
      result = _k.mul(topE.div(_emissionRate.mul(bottomE)));
    }
    return result;
  }
```
One possible scenarios where `purchasePrice` could approach near-zero values is:
_k = 1e18 (the initial price of the CGDA)
_decayConstant = 0.00001 (a very small decay constant)
_amount = 1e18 (the amount of tokens to purchase which has reportedly become a rare commodity)
_emissionRate = 1e20 (a significantly large, but more practical emission rate)
_timeSinceLastAuctionStart = 3600 (equivalent to 1 hour)

The small `_decayConstant`, along with the relatively short `_timeSinceLastAuctionStart`, results in `bottomE` being close to 1. `_emissionRate` is significantly larger than `_amount`, making `topE` also close to 1. As a result, this combination of factors drives the `purchasePrice` towards near-zero values. 

Please note that this is only one of many possible scenarios where the purchase price could approach near-zero values.
VaultBooster liquidations can revert due to discrepancy in liquidatable balance value
Audit: 2023-08-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/GenerationSoftware/pt-v5-cgda-liquidator/blob/7f95bcacd4a566c2becb98d55c1886cadbaa8897/src/LiquidationPair.sol#L266-L270
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L201-L203
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L249-L257
https://github.com/GenerationSoftware/pt-v5-vault-boost/blob/9d640051ab61a0fdbcc9500814b7f8242db9aec2/src/VaultBooster.sol#L276-L279


# Vulnerability details

## Impact
When a user/bot wants to perform a liquidation for a liquidation pair, they can call view methods like `maxAmountOut` to see what balance of the output token they are able to liquidate at the current time. This makes an underlying call to `liquidatableBalanceOf` that reports the maximum balance of output token that is held by the Vault or VaultBooster.

However the VaultBooster contract has an issue where the return value of `liquidatableBalanceOf` can actually be higher than the balance of output token held by the contract. As a result, a user/bot might try to perform a liquidation for the `liquidatableBalanceOf` of the VaultBooster, but this call will revert because the resulting token transfer will revert due to insufficient balance.

## Proof of Concept
A liquidator is able to compute the profitability of a liquidation before performing the action by calling view methods like `maxAmountOut`, `maxAmountIn`, `computeExactAmountIn` and `estimateAmountOut`. All of these view methods make an underlying call to `_maxAmountOut`:

```
  /// @notice Computes the maximum amount of output tokens that can be purchased
  /// @return Maximum amount of output tokens
  function _maxAmountOut() internal returns (uint256) {
    uint emissions = uint(convert(_emissionRate.mul(_getElapsedTime())));
    uint liquidatable = source.liquidatableBalanceOf(tokenOut);
    return emissions > liquidatable ? liquidatable : emissions;
  }
```

This method returns the maximum amount of output tokens that can be purchased based on the emission rate and the liquidatable balance of the source (i.e. the Vault or VaultBooster). In the case where the emissions have exceeded the liquidatable balance of the source, the max amount that can be purchased is equal to the liquidatable balance of the source.

For the VaultBooster contract, `liquidatableBalanceOf` looks like:

```
  function liquidatableBalanceOf(address _tokenOut) external override returns (uint256) {
    return _accrue(IERC20(_tokenOut));
  }
```

where the underlying call to `_accrue` looks like:

```
  function _accrue(IERC20 _tokenOut) internal returns (uint256) {
    uint256 available = _computeAvailable(_tokenOut);
    _boosts[_tokenOut].available = available.toUint144();
    _boosts[_tokenOut].lastAccruedAt = uint48(block.timestamp);

    emit BoostAccrued(_tokenOut, available);

    return available;
  }
```

As you can see, the `l