xanoutas/solidity-security-findings huggingface dataset:
Hardcoded `VALID_TIME_PERIOD` is not suitable in all cases. 
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PriceFeed.sol#L35


# Vulnerability details

## Impact
In `IPyth.sol`, the following [comment](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/vendors/IPyth.sol#L22) is provided on `getPriceNoOlderThan()`:
```solidity
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
```
Now, according to [Ipyth Documentation](https://docs.fringe.fi/v2-smart-contract-documentation/readme/priceoracle/priceproviders/pythpriceprovider):
> `setValidTimePeriod()`: Sets validTimePeriod used to check if price is older than  validTimePeriod  to perform price update. Only the moderator can call this function.

However in `PriceFeed.sol`, the `VALID_TIME_PERIOD` constant is hardcoded to `5 minutes`. While this may be suitable for some use cases, it lacks `flexibility` and adaptability for different scenarios or changing requirements.

Different use cases may require different `validity periods` for price data.
---> The hardcoded value may not be optimal for all scenarios, potentially leading to less accurate or outdated price data. 


## Proof of Concept
The [VALID_TIME_PERIOD](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PriceFeed.sol#L35) constant is hardcoded to `5 minutes`.
```solidity
uint256 private constant VALID_TIME_PERIOD = 5 * 60;
```
This `VALID_TIME_PERIOD` is used in the [getSqrtPrice()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PriceFeed.sol#L48) function to ensure that the price data retrieved from the `IPyth` contract is not older than the specified validity period.
```solidity
function getSqrtPrice() external view returns (uint256 sqrtPrice) {
        (, int256 quoteAnswer,,,) = AggregatorV3Interface(_quotePriceFeed).latestRoundData();

        IPyth.Price memory basePrice = IPyth(_pyth).getPriceNoOlderThan(_priceId, VALID_TIME_PERIOD); // @audit-issue used here

        //...
    }
```
- If the `VALID_TIME_PERIOD` is too long, the price data may be outdated.
---> This can lead to inaccurate price calculations, affecting the reliability of the protocol.

- If the `VALID_TIME_PERIOD` is too short, the contract may frequently fail to find price data within the validity period.
---> This can result in frequent reverts of the `getSqrtPrice()`

## Tools Used
Manual Review

## Recommended Mitigation Steps
Make the hardcoded `VALID_TIME_PERIOD` configurable.

```solidity
// @audit Replace the hardcoded constant with a state variable that can be updated.
uint256 private _validTimePeriod;

// @audit Initialize the _validTimePeriod in the constructor.
constructor(address quotePrice, address pyth, bytes32 priceId, uint256 decimalsDiff, uint256 validTimePeriod) {
    //...
    _validTimePeriod = validTimePeriod;
}


// @audit Add a function to update the _
insufficient slippage specification in uniswap
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/settlements/UniswapSettlement.sol#L34
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/settlements/UniswapSettlement.sol#L50


# Vulnerability details

## Impact
Having block.timestamp for slippage specification is not a ideal check, since block.timestamp is the time when the function is executed. The transaction can be sitting in the mempool & executed after a long time.
This can be done easily via a MEV. A MEV bot detects the pending transaction. Since the outdated maximum slippage value now allows for high slippage, the bot sandwiches Alice, resulting in significant profit for the bot and significant loss for Alice.

## Proof of Concept

AMMs provide their users with an option to limit the execution of their pending actions, such as swaps or adding and removing liquidity. The most common solution is to include a deadline timestamp as a parameter (for example see Uniswap V2 and Uniswap V3). If such an option is not present, users can unknowingly perform bad trades:

But since the protocol has a specific deadline check while checking for [order verification](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/markets/gamma/GammaTradeMarket.sol#L445) which checks the deadline in--> [Validate()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/orders/ResolvedOrder.sol#L28)

Which basically means that a miner or mev can execute the order through uniswap just before the deadline check.

## Tools Used
Uniswap 

## Recommended Mitigation Steps
Have a deadline check like uniswap. OR 
decode the deadline parameter from OrderInfo struct and pass it along the swap.




## Assessed type

Uniswap
`updateFeeRatio()` does not collect previous fees before setting a new `feeRatio`
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PredyPool.sol#L147-L149
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/logic/AddPairLogic.sol#L96-L102


# Vulnerability details

## Impact
Changing `feeRatio` will recalculate previously accumulated `fees` if they were not accrued.

## Proof of Concept
According to the protocol, `interest rate` should not be applied multiple times in the same block. This is implemented as follows within [applyInterestForToken()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/ApplyInterestLib.sol#L31-L34):
```solidity
// avoid applying interest rate multiple times in the same block
        if (pairStatus.lastUpdateTimestamp >= block.timestamp) {
            return;
        }
```
When this function is called, it calculates `totalProtocolFee` by invoking [applyInterestForPoolStatus()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/ApplyInterestLib.sol#L36-L40). This calculation depends on `pairStatus.feeRatio`.
```solidity
        (uint256 interestRateQuote, uint256 protocolFeeQuote) =
            applyInterestForPoolStatus(pairStatus.quotePool, pairStatus.lastUpdateTimestamp, pairStatus.feeRatio);

        (uint256 interestRateBase, uint256 protocolFeeBase) =
            applyInterestForPoolStatus(pairStatus.basePool, pairStatus.lastUpdateTimestamp, pairStatus.feeRatio);

```

The issue is that when [updateFeeRatio()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PredyPool.sol#L147-L149) is called by the `poolOwner`, it does not call the `applyInterestForToken()` to collect the fees within this current block (that is if it had not been called yet by other functions like `withdraw()` or `supply()`).

```solidity
    function updateFeeRatio(uint256 pairId, uint8 feeRatio) external onlyPoolOwner(pairId) {
        AddPairLogic.updateFeeRatio(globalData.pairs[pairId], feeRatio);
    }
```
Here it internally calls [AddPairLogic.updateFeeRatio()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/logic/AddPairLogic.sol#L96-L102); 
```solidity
    function updateFeeRatio(DataType.PairStatus storage _pairStatus, uint8 _feeRatio) external {
        validateFeeRatio(_feeRatio);

        _pairStatus.feeRatio = _feeRatio;

        emit FeeRatioUpdated(_pairStatus.id, _feeRatio);
    }
```

This means that the new `feeRatio` will be used to calculate `totalProtocolFee` for the period before it was updated. This can result in an incorrect amount of fees being accrued.

---> It would be more appropriate if a newly updated `feeRatio` is used starting from the next block.

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider collecting fees before setting new `feeRatio`:
```diff
    functi
`callUniswapObserve` will show incorrect price for negative ticks because it does not round up for them
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L35-L74


# Vulnerability details



## Impact
In case if `int24(tickCumulatives[i] - tickCumulatives[i+1])` is negative and `((tickCumulatives[i] - tickCumulatives[i+1]) % secondsAgo != 0`, then returned tick will be bigger than it should be which opens possibility for some price manipulations and arbitrage opportunities.


## Proof of Concept
Take a look at: https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L35-L74

```solidity
function callUniswapObserve(IUniswapV3Pool uniswapPool, uint256 ago) internal view returns (uint160, uint256) {
        uint32[] memory secondsAgos = new uint32[](2);

        secondsAgos[0] = uint32(ago);
        secondsAgos[1] = 0;

        (bool success, bytes memory data) =
            address(uniswapPool).staticcall(abi.encodeWithSelector(IUniswapV3PoolOracle.observe.selector, secondsAgos));

        if (!success) {
            if (keccak256(data) != keccak256(abi.encodeWithSignature("Error(string)", "OLD"))) {
                revertBytes(data);
            }

            (,, uint16 index, uint16 cardinality,,,) = uniswapPool.slot0();

            (uint32 oldestAvailableAge,,, bool initialized) = uniswapPool.observations((index + 1) % cardinality);

            if (!initialized) {
                (oldestAvailableAge,,,) = uniswapPool.observations(0);
            }

            ago = block.timestamp - oldestAvailableAge;
            secondsAgos[0] = uint32(ago);

            (success, data) = address(uniswapPool).staticcall(
                abi.encodeWithSelector(IUniswapV3PoolOracle.observe.selector, secondsAgos)
            );
            if (!success) {
                revertBytes(data);
            }
        }

        int56[] memory tickCumulatives = abi.decode(data, (int56[]));

        int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int256(ago)));

        uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick);

        return (sqrtPriceX96, ago);
```
This function is used to get twap price tick using uniswap. It gets `tickCumulatives` array which is then used to calculate `int24 tick`. 

The problem is that if `int24(tickCumulatives[1] - tickCumulatives[0])` is negative, then the tick should be rounded down as it's done in the[ uniswap library](https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/OracleLibrary.sol#L36).

As result, in case if `int24(tickCumulatives[1] - tickCumulatives[0])` is negative and `(tickCumulatives[1] - tickCumulatives[0]) % secondsAgo != 0,` then returned tick will be bigger then it should be, which places Predy protocol wanting prices to be right not be able to achieve this goal.


## Tools Used
Manual review

## Recommeded Mitigation Steps
Add this line.
```solidity
if (tickCumulatives[1] - tickCumulatives[0] < 0 && (tickCumulatives[1] - tick
Use of slot0 can lead to price manipulations
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L12-L14


# Vulnerability details




## Impact

In the `UniHelper.sol` library, the functions `getSqrtPrice` and `getSqrtTWAP` use the `slot0` function of the `IUniswapV3Pool` interface to get the value of `sqrtPriceX96`. The `sqrtPriceX96` obtained from `UniswapV3.slot0` represents the most recent data point and can be easily manipulated via MEV (Miner Extractable Value) bots and flash loans. This manipulation can lead to sandwich attacks, where the price is artificially inflated or deflated before and after a transaction, causing a loss of funds when interacting with Uniswap's swap functions or any other logic that relies on the manipulated price data.


## Proof of Concept

You can see the `getSqrtPrice` function in the `UniHelper.sol`:

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L12-L14

```solidity
function getSqrtPrice(address uniswapPoolAddress) internal view returns (uint160 sqrtPrice) {
    (sqrtPrice,,,,,,) = IUniswapV3Pool(uniswapPoolAddress).slot0();
}
```

The function uses the `sqrtPrice` obtained from `Uniswap.slot0`.

Getting prices using slot0 is highly frowned upon. An attacker can simply manipulate the `sqrtPrice` by using MEV bots or flash loans. If any function relying on `getSqrtPrice` or `getSqrtTWAP` is called with the manipulated `sqrtPrice`, the token will be bought or sold at an artificially inflated or deflated price. The attacker can then back-run the transaction to sell or buy back the tokens at the manipulated price, thereby making a profit while causing a loss to whoever called those functions.


The value if manipulated is used in  `convertSqrtPrice` of  `UniHelper` and this `convertSqrtPrice` is queried by [`getSqrtPrice` function in `Trade.sol`.](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Trade.sol#L113)

`getSqrtPrice` is used during the execution of trades to determine the current price of a trading pair in the Uniswap pool. This price is essential for calculating how much of one token a user receives when swapping another token, ensuring that the trade is executed at a fair market rate according to the liquidity available in the pool. So as we can see, any value gotten as a result of a manipulated `Slot0` goes a long way to corrupt the protocol.


## Tools used
- Manual review
- Please refer to a similar issue [here](https://github.com/code-423n4/2023-05-maia-findings/issues/823)

## Recommended Mitigation Steps
Use The `TWAP` to get the value of `sqrtPrice`.



## Assessed type

Uniswap
Chainlink’s latestRoundData might return stale or incorrect results
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/PriceFeed.sol#L46


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

The ChainlinkAdapter calls out to a Chainlink oracle receiving the latestRoundData(). If there is a problem with Chainlink starting a new round and finding consensus on the new value for the oracle (e.g. Chainlink nodes abandon the oracle, chain congestion, vulnerability/attacks on the chainlink system) consumers of this contract may continue using outdated stale or incorrect data (if oracles are unable to submit no new round is started).

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

 function getSqrtPrice() external view returns (uint256 sqrtPrice) {
        (, int256 quoteAnswer,,,) = AggregatorV3Interface(_quotePriceFeed).latestRoundData();


## Tools Used

## Recommended Mitigation Steps
...
( roundId, rawPrice, , updateTime, answeredInRound ) = AggregatorV3Interface(XXXXX).latestRoundData();
require(rawPrice > 0, "Chainlink price <= 0");
require(updateTime != 0, "Incomplete round");
require(answeredInRound >= roundId, "Stale price");
...


## Assessed type

Context
Multiple instances of usage of `slot0` to calculate square root price, which is easily manipulatable
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

ttps://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Perp.sol#L206
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/UniHelper.sol#L13-L15
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L49


# Vulnerability details

## Impact
There are multiple instances where `Uniswap's slot0` is used to calculate the `square root price`.

This is dangerous as `slot0` returns the pool’s spot price, which can be manipulated by buying and selling assets within the pool using flash loans, since this price is given by the current ratio of reserves in the pool.

An attacker can make the spot price favorable for them to acheive their desired outcome. 

## Proof of Concept
As mentioned, `slot0` is used to fetch the spot price in multiple instances throughout the protocol:

[Perp.sol#L206](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Perp.sol#L206)
```javascript
(uint160 currentSqrtPrice, int24 currentTick,,,,,) = IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0();
```

[UniHelper.sol#L13-L15)](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/UniHelper.sol#L13-L15)
```javascript
    function getSqrtPrice(address uniswapPoolAddress) internal view returns (uint160 sqrtPrice) {
        (sqrtPrice,,,,,,) = IUniswapV3Pool(uniswapPoolAddress).slot0();
    }
```

An example of where this is used is when trades are executed:

[Trade.sol#L49](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L49)
```javascript
tradeResult.sqrtPrice = getSqrtPrice(pairStatus.sqrtAssetStatus.uniswapPool, pairStatus.isQuoteZero);
```

An attacker can manipulate the spot price via flash loans to make trades favorable for them.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Use the TWAP instead of slot0 when obtaining the price


## Assessed type

Uniswap
Malicious user can create positions that can never be liquidated, breaking protocol invariant
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/GammaTradeMarket.sol#L143-L144
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/logic/LiquidationLogic.sol#L67
https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/GammaTradeMarket.sol#L99
https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/GammaTradeMarket.sol#L402-L406
https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/ArrayLib.sol#L9-L31


# Vulnerability details

## Impact
The protocol mentions the following in the contest [README](https://code4rena.com/audits/2024-05-predy#top:~:text=Assuming%20sufficient%20liquidity%2C%20safe%20vaults%20cannot%20be%20liquidated%2C%20whereas%20unsafe%20vaults%20can%20be%20liquidated.%20This%20ensures%20that%20only%20positions%20that%20pose%20a%20risk%20to%20the%20system%20are%20targeted%20for%20liquidation%2C%20maintaining%20the%20integrity%20of%20the%20platform.) as one of the main invariants: `"Assuming sufficient liquidity, safe vaults cannot be liquidated, whereas unsafe vaults can be liquidated. This ensures that only positions that pose a risk to the system are targeted for liquidation, maintaining the integrity of the platform.".`

When liquidating an unsafe vault (position), it is removed from the list of the user's positions. The index of the position is searched by looping through all of the user's positions. Once the index is found, the position is removed from the list.

A malicious user can create enough positions that looping through the list of their positions will cause DoS due to insufficient gas.

Since their unsafe positions can never be liquidated, this invariant will be broken.

## Proof of Concept
Any user can liquidate a bad position in either `Gamma` or `Perp` markets.

[GammaTradeMarket.sol#L143-L144](https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/GammaTradeMarket.sol#L143-L144)
```javascript
    tradeResult =
        _predyPool.execLiquidationCall(vaultId, closeRatio, _getSettlementDataFromV3(settlementParams, msg.sender));
```

When liquidating a position, a trade is executed to settle the position.

[LiquidationLogic.sol#L67](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/logic/LiquidationLogic.sol#L67)
```javascript
tradeResult = Trade.trade(globalData, tradeParams, settlementData);
```

During the trade callback, the position is removed

[GammaTradeMarket.sol#L99](https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/GammaTradeMarket.sol#L99)
```javascript
 _removePosition(tradeParams.vaultId);
```

When the position is removed, `removeItem` is called with the entire list of the trader's positions `positionIDs[trader]` (who is being liquidated on behalf of the caller)

[GammaTradeMarket.sol#L402-L406)](https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/gamma/GammaTradeMarket.sol#L402-L406)
```javascript
positionIDs[trader].removeItem(po
Using `block.timestamp` as deadline is dangerous
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L53-L60
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L94
https://github.com/code-423n4/2024-05-predy/blob/main/src/base/SettlementCallbackLib.sol#L113-L120
https://github.com/code-423n4/2024-05-predy/blob/main/src/base/SettlementCallbackLib.sol#L160-L167
https://github.com/code-423n4/2024-05-predy/blob/main/src/settlements/UniswapSettlement.sol#L33-L35
https://github.com/code-423n4/2024-05-predy/blob/main/src/settlements/UniswapSettlement.sol#L49-L51


# Vulnerability details

## Impact
The protocol utilizes Uniswap's `swapExactIn` and `swapExactOut` for executing trades.

The problem is that the deadline is always set to `block.timestamp`, which completely defeats the purpose of having a `deadline` parameter.

This is a [known issue](https://github.com/code-423n4/2022-12-backed-findings/issues/64), where a malicious user or MEV bot can execute the transaction at a later time that benefits them.

Due to a lack of deadline, these swaps may cause a loss for the protocol.

## Proof of Concept
When trades are executed, a swap is initiated

[Trade.sol#L53-L60](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L53-L60)
```javascript
    SwapStableResult memory swapResult = swap(
        globalData,
        tradeParams.pairId,
        SwapStableResult(-tradeParams.tradeAmount, underlyingAmountForSqrt, realizedFee.feeAmountBase, 0),
        settlementData,
        tradeResult.sqrtPrice,
        tradeParams.vaultId
    );
```

The swap function will make a call to the settlement callback

[Trade.sol#L94](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L94)
```javascript
globalData.callSettlementCallback(settlementData, totalBaseAmount);
```

During the callback, depending on if base tokens are being bought or sold, `swapExactIn` or `swapExactOut` is called

[SettlementCallbackLib.sol#L113-L120](https://github.com/code-423n4/2024-05-predy/blob/main/src/base/SettlementCallbackLib.sol#L113-L120)
```javascript
    uint256 quoteAmountFromUni = ISettlement(settlementParams.contractAddress).swapExactIn(
        quoteToken,
        baseToken,
        settlementParams.encodedData,
        sellAmount,
        settlementParams.maxQuoteAmount,
        address(this)
    );
```

[SettlementCallbackLib.sol#L160-L167](https://github.com/code-423n4/2024-05-predy/blob/main/src/base/SettlementCallbackLib.sol#L160-L167)
```javascript
    uint256 quoteAmountToUni = ISettlement(settlementParams.contractAddress).swapExactOut(
        quoteToken,
        baseToken,
        settlementParams.encodedData,
        buyAmount,
        settlementParams.maxQuoteAmount,
        address(predyPool)
    );
```

The `Uniswap Settlement` has `block.timestamp` always set for the deadline

[UniswapSettlement.sol#L33-L35](https://github.com/code-423n4/2024-05-predy/blob/main/src/settlements/UniswapSettlement.sol#