xanoutas/solidity-security-findings huggingface dataset:
When initializing a pool, no value is set for its `feeProtocol` variable  resulting in revenue loss for the protocol
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/4a40eba36bc47eba8179d4f6203a4b84561a4415/contracts/CL/core/RamsesV3Pool.sol#L152-L174


# Vulnerability details

## Impact

Because `feeProtocol` is set to 0 when initializing a new pool, all swapFees will remain in the pool for LP providers and one of the fees will be given to the protocol which will result in revenue loss of the protocol and thereby revenue loss for the treasury and the pools gauge.


## Proof of Concept
 
Every time a user makes a swap in a pool, he pays swapFees. These swaFees are split between the protocol and the LP providers. The variable `feeProtocol` in the pool determine what percentage of the swapFees are supposed to go to the protocol. This fee is set by calling `setFeeProtocol` which in turn call the `RamsesV3PoolFactory` to determine the `feeProtocol` for the pool.

The issue arises from the fact that when a pool is initialized, the feeProtocol is set to 0 instead of the value saved in the `RamsesV3PoolFactory`. This means that the percentage of the swapFees send to the protocol are 0 and all fees remain in the pool for the LP provider. Since the fees for the protocol go partially to the treasury and partially to the gauge of the pool to incentivise Ramses token holders, not setting the `feeProtocol` for a new pool will result in revenue loss of the above-mentioned parties.`

## Recommended Mitigation Steps

When initializing a pool, make sure to determine the value for `feeProtocol` saved in the `RamsesV3PoolFactory` and setting the `feeProtocol` accordingly:

```diff
    function initialize(uint160 sqrtPriceX96) external {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        if ($.slot0.sqrtPriceX96 != 0) revert AI();

        int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);

        (uint16 cardinality, uint16 cardinalityNext) = Oracle.initialize($.observations, 0);
+       uint8 _feeProtocol = IRamsesV3Factory(factory).poolFeeProtocol(address(this));


        _advancePeriod();

        $.slot0 = Slot0({
            sqrtPriceX96: sqrtPriceX96,
            tick: tick,
            observationIndex: 0,
            observationCardinality: cardinality,
            observationCardinalityNext: cardinalityNext,
-           feeProtocol: 0,
+           feeProtocol: _feeProtocol,
            unlocked: true
        });

        emit Initialize(sqrtPriceX96, tick);
    }
```

Also make the `setFeeProtocol` function public so it is callable in the constructor.



## Assessed type

Other
Loss of funds for positions created later than the gauge of a pool
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L510-L513


# Vulnerability details

## Impact
The `firstPeriod` value of the gauge is used to determine the `lastClaim` period of every position which has not claimed any rewards yet. This means that to be able to claim rewards, users who create positions after the creation of the gauge need to cycling thorough past periods where their position did not exist yet and pay the associated fees. Those cost can exceed the value of the claimed rewards by a multiple. 



## Proof of Concept
 
Every pool has one gauge. When this gauge is created, the current period is set as `firstPeriode` in the [constructor]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L87):
```
firstPeriod = _blockTimestamp() / WEEK;
```

This value is then used to determine the last period rewards were claimed for a position. If a position has not claimed rewards yet, the `lastClaim` period is set to the `firstPeriod`:

```solidity
            lastClaim = Math.max(
                lastClaimByToken[tokens[i]][_positionHash],
                firstPeriod
            );
```
The value of `lastClaim` is then used to cycle through the periods which have past since the `lastClaim` and accumulate (in case of the view function [earned]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L200-L230) or send the earned reward tokens to the user (in case of the internal function [_getAllRewards]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L493C14-L532)).

The issue arises from the fact that all positions who claim rewards for a reward token the first time use the `firstPeriode` value of the gauge, even if they were created many periods later than the gauge. This means that every user who wants to claim rewards for a reward token for the first time will need to cycle through all periods which have past since the `firstPeriode` of the gauge. This happens even though their position might not have been existent during these periods and the resulting rewards will be 0. This is made even worse by the fact that, since the function [cachePeriodEarned]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L278C14-L335) which determines the rewards is called with the value `caching` set to true, for each of the past periods, values for `periodAmountsWritten` and ` periodNfpSecondsX96` are written to storage:

```solidity
            if (period < _blockTimestamp() / WEEK && caching) {
                periodAmountsWritten[period][_positionHash] = true;
                periodNfpSecondsX96[period][
                    _positionHash
Having only one firstPeriod per gauge results in the waste of unnecessary fee cost for reward tokens added later
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L562-L569
https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L510-L513


# Vulnerability details

## Impact
Because every gauge uses one central `firstPeriod` value for all reward tokens, users who claim reward for any token the first time will try to claim rewards starting from this one `firstPeriode`. This leads to significant gas cost and therefore loss of funds for the users if a reward token is added later to the gauge since all periods starting from the `firstPeriode` will be checked. These cost can get very high and can in some cases exceed the value of the claimed rewards.  


## Proof of Concept

Every pool has one gauge. When this gauge is created, the current period is set as `firstPeriode` in the [constructor]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L87):
```
firstPeriod = _blockTimestamp() / WEEK;
```

This value is then used to determine the last period in which rewards were claimed for a position. If a position has not claimed rewards for a reward token yet, the `lastClaim` period is set to the `firstPeriod`:

```solidity
            lastClaim = Math.max(
                lastClaimByToken[tokens[i]][_positionHash],
                firstPeriod
            );
```
The value of `lastClaim` is then used to cycle through the periods which have past since the `lastClaim` and accumulate (in case of the view function [earned]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L200-L230) or send the earned reward tokens to the user (in case of the internal function [_getAllRewards]( https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L493C14-L532)).

The issue arises from the fact that all reward tokens use the same `firstPeriode` value, even if they were added as a reward token to the gauge later. This means that every user who wants to claim rewards for a reward token which was added later to the gauge will need to cycle through all periods which have past since the `firstPeriode` of the gauge. This will happen even thought the token was not a reward token back then and the result for the tokens earned will obviously be 0. 

Example:
The gauge for a pool is created in period 100 and therefore its `firstPeriod` variable is set to 100. Nearly 2 years pass, and we are now in period 200. A new reward token (tokenA) is added to the gauge to incentivise uses even further. One period passes and users want to claim there tokenA rewards. Since no user has claimed rewards for tokenA before, the value for `lastClaimByToken` is 0 for all user. This means, when they claim their toke
Incorrect fee calculation in flash()
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/core/RamsesV3Pool.sol#L684-L725


# Vulnerability details

## Proof of Concept
In RamsesV3Pool, we support flashloan feature via function flash(). The contract will charge a part of flashloan fee. And these flashloan fee will be splitted two part. One part belongs to the protocol and the other part belongs to the LP holders.

The problem is that we make the wrong calculation when we distribute the flashloan fees. The variable `$.slot0.feeProtocol`'s meaning is different with Uniswap V3 pool. 
In Rameses, the `feeProtocol` means that the fee's percentage which the protocol will charge. For example: `feeProtocol = 50`, it means that the protocol will take one half of the whole fees.
The fee's split calculation in flash() is as below `uint256 pFees0 = feeProtocol == 0 ? 0 : paid0 / feeProtocol;`. This is incorrect. If the `feeProtocol` is 50, the protocol can only get 1/50 of the fees.

```javascript
    function setPoolFeeProtocol(address pool, uint8 _feeProtocol) external restricted {
        require(_feeProtocol <= 100, FTL());
        uint8 feeProtocolOld = poolFeeProtocol(pool);
        _poolFeeProtocol[pool] = _feeProtocol;
        emit SetPoolFeeProtocol(pool, feeProtocolOld, _feeProtocol == 0 ? feeProtocol : _feeProtocol);

        IRamsesV3Pool(pool).setFeeProtocol();
    }
```
```javascript
    function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external override lock {
        ...
        unchecked {
            ...
            // @audit is this correct, it's not correct
            // We should use paid0 * feeProtocol / 100.
            uint8 feeProtocol = $.slot0.feeProtocol;
            if (paid0 > 0) {
@>                uint256 pFees0 = feeProtocol == 0 ? 0 : paid0 / feeProtocol;
                if (uint128(pFees0) > 0) $.protocolFees.token0 += uint128(pFees0);
                $.feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - pFees0, FixedPoint128.Q128, _liquidity);
            }
            ...
            emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);
        }
    }
```

## Recommended Mitigation Steps
Correct the calculation of the protocol flashloan fees.
```diff
             uint8 feeProtocol = $.slot0.feeProtocol;
             if (paid0 > 0) {
-                uint256 pFees0 = feeProtocol == 0 ? 0 : paid0 / feeProtocol;
+                uint256 pFees0 = feeProtocol == 0 ? 0 : paid0 * feeProtocol/100;
                 if (uint128(pFees0) > 0) $.protocolFees.token0 += uint128(pFees0);
                 $.feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - pFees0, FixedPoint128.Q128, _liquidity);
             }
```





## Assessed type

Error
Non-owners can claim rewards for NFT positions in GaugeV3
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L436-L441


# Vulnerability details

`getReward` function in the GaugeV3 contract has an incorrect access control check that allows non-owners of an NFT position to claim rewards for that position. The function's require statement checks if the caller is either the owner, the approved operator, or an account that has been granted "approved for all" status by the owner: [GaugeV3.sol#L436-L441](https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L436-L441)
```solidity
require(
    msg.sender == owner ||
        msg.sender == operator ||
        _nfpManager.isApprovedForAll(owner, msg.sender),
    "Not authorized"
);
```
However, the intended behavior is that only the direct owner of an NFT position should be able to claim rewards for that position. Allowing operators and approved accounts to claim rewards violates this expectation and can lead to unintended distribution of rewards.

This check allows the following accounts to claim rewards for an NFT position:

1. The owner of the NFT
2. The approved operator of the NFT
3. Any account that the owner has set as "approved for all"

However, the `onlyOwnerCanClaim` rule specifies that only the direct owner should be able to claim rewards. Allowing operators and approved accounts to claim is unintended behavior.

## Impact
Owners may lose out on rewards if a non-owner claims the rewards before the owner does.

## Proof of Concept
The bug will occur whenever a non-owner account that is either the approved operator or "approved for all" for an NFT position calls getReward for that position.

While this requires specific access permissions, it's not uncommon for NFT owners to grant operator or "approved for all" status to other accounts (e.g., marketplaces, wallets, etc.). Therefore, the likelihood of this bug being exploited in the wild is relatively high.

Demonstrating the bug
```solidity
// 1. Deploy GaugeV3
GaugeV3 gauge = new GaugeV3(...);

// 2. Mint an NFT position with ID 1 to Alice
INonfungiblePositionManager nfpManager = gauge.nfpManager();
nfpManager.mint(Alice, 1, ...);

// 3. Alice approves Bob as an operator for the NFT
nfpManager.approve(Bob, 1);

// 4. Bob claims rewards for Alice's NFT
address[] memory tokens = new address[](2);
tokens[0] = gauge.token0();
tokens[1] = gauge.token1();
gauge.getReward(1, tokens);

// Bob successfully claims rewards for Alice's NFT, violating the onlyOwnerCanClaim rule
```

## Tools Used
VsCode

## Recommended Mitigation Steps
Update the access control check in the getReward function to only allow the direct owner of an NFT position to claim rewards
```diff
- require(
-     msg.sender == owner ||
-         msg.sender == operator ||
-         _nfpManager.isApprovedForAll(owner, msg.sender),
-     "Not authorized"
- );
+ require(msg.sen
Incorrect check in `setFee()` restricts flexibility in `$.fee` 
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/core/libraries/ProtocolActions.sol#L78


# Vulnerability details

## Proof of Concept
The [`setFee()`](https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/core/libraries/ProtocolActions.sol#L75-L82) function is esential for setting `$.fee`:
```solidity
    function setFee(uint24 _fee, address factory) external {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();
        if (msg.sender != factory) revert Unauthorized();
>>      if (_fee > 100000) revert InvalidFee();
        uint24 _oldFee = $.fee;
>>      $.fee = _fee;
        emit FeeAdjustment(_oldFee, _fee);
    }
```
Here, the `fee` is constrained by the check `if (_fee > 100000) revert InvalidFee();`, where the maximum allowed value of `_fee` is `100,000`, corresponding to a `10% fee` in basis points. This check is too restrictive and does not reflect real-world requirements, where fees greater than `10%` might be justified.

However, the check requires that `_fee` should not exceed `100000` which is `10%` considering its application. Here is how is is used in [`flash()`](https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/core/RamsesV3Pool.sol#L690-L691) to detemine `fee0` and `fee1` from `amount0` and `amount1`:
```solidity
        uint256 fee0 = FullMath.mulDivRoundingUp(amount0, $.fee, 1e6);
        uint256 fee1 = FullMath.mulDivRoundingUp(amount1, $.fee, 1e6);
```
The fees (`fee0` and `fee1`) are computed by multiplying the loan amount by `$.fee` and dividing by `1_000_000` (basis points).
With the current cap, the `maximum fee` is limited to `10% `of the `amount` (`100000/1e6 = 0.1`), even though there might be scenarios where higher fees are necessary.

**Note**:
Indeed `ProtocolActions.sol` is OOS but the `setFee()` in it is in only callabe from `factory` contract (*in scope*) and as seen, the set value impacts operation in the pool contract (*also in scope*). As such, it is worth reporting.

## Impact
The current `fee` cap in the `ProtocolActions.setFee()` function is limited to `10%`, which is unnecessarily restrictive and does not align with the intended design or flexibility required by the protocol. This constraint could prevent setting higher fees for certain operations where a higher fee might be necessary, leading to reduced protocol functionality or inefficient capital use.

If the protocol needs to charge more than `10%`, this limitation restricts potential revenue for the protocol, especially in scenarios that justify higher fees.

## Recommended Mitigation Steps
Set an appropriate upper limit that reflects the highest realistic fee the protocol would want to charge is crucial.
```diff
    function setFee(uint24 _fee, address factory) external {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage
No check forAuthorizedForToken in increaseLiquidity.
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/main/contracts/CL/periphery/NonfungiblePositionManager.sol#L202


# Vulnerability details

## Proof of Concept
Explain and rationalize the potential impact. Provide:
- Direct links to all referenced code in GitHub
- Screenshots, logs, or any other relevant proof that illustrates the concept
- Any additional research or code needed to validate the claims made in your submission

There is no check for forAuthorizedForToken in the increaseLiquidity.
 function increaseLiquidity(
        IncreaseLiquidityParams calldata params
    )
        external
        payable
        override
        checkDeadline(params.deadline)
        returns (uint128 liquidity, uint256 amount0, uint256 amount1)
    {
        Position storage position = _positions[params.tokenId];

        PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];

        IRamsesV3Pool pool;
        (liquidity, amount0, amount1, pool) = addLiquidity(
            AddLiquidityParams({
                token0: poolKey.token0,
                token1: poolKey.token1,
                tickSpacing: poolKey.tickSpacing,
                tickLower: position.tickLower,
                tickUpper: position.tickUpper,
                amount0Desired: params.amount0Desired,
                amount1Desired: params.amount1Desired,
                amount0Min: params.amount0Min,
                amount1Min: params.amount1Min,
                recipient: address(this),
                index: params.tokenId
            })
        );

## Recommended Mitigation Steps
isAuthorizedForToken(params.tokenId)



## Assessed type

Context
Missing Reentrancy Lock on cachePeriodEarned Function in GaugeV3 Contract
Audit: 2024-10-ramses-exchange-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L277-L335


# Vulnerability details

## Proof of Concept
Explain and rationalize the potential impact. Provide:
- Direct links to all referenced code in GitHub
- Screenshots, logs, or any other relevant proof that illustrates the concept
- Any additional research or code needed to validate the claims made in your submission

### Impact
The cachePeriodEarned function in the GaugeV3 contract lacks a reentrancy lock guard (lock modifier).
Allowing external contracts or users to call it and manipulate internal state variables without restriction. 
Specifically, unauthorized callers can invoke this function with the caching parameter set to true.
This causing unintended writes to storage mappings such as periodAmountsWritten and periodNfpSecondsX96. 

### Vulnerable Code
```Solidity
// https://github.com/code-423n4/2024-10-ramses-exchange/blob/236e9e9e0cf452828ab82620b6c36c1e6c7bb441/contracts/CL/gauge/GaugeV3.sol#L277-L287

/// @dev used by getReward() and saves gas by saving states
function cachePeriodEarned(
        uint256 period,
        address token,
        address owner,
        uint256 index,
        int24 tickLower,
        int24 tickUpper,
        bool caching
    ) public override returns (uint256 amount) {
      uint256 periodSecondsInsideX96;
```

### Proof of Concept
An external contract can call the cachePeriodEarned function with the caching parameter set to true.
This causing unauthorized modifications to the contract’s internal state:
```Solidity
pragma solidity ^0.8.13;

import "./GaugeV3.sol";

contract MaliciousContract {
    GaugeV3 public gaugeV3;

    constructor(address _gaugeV3Address) {
        gaugeV3 = GaugeV3(_gaugeV3Address);
    }

    function attackCachePeriodEarned(
        uint256 period,
        address token,
        address owner,
        uint256 index,
        int24 tickLower,
        int24 tickUpper
    ) public {
        // Calling cachePeriodEarned with caching=true to manipulate internal state
        gaugeV3.cachePeriodEarned(period, token, owner, index, tickLower, tickUpper, true);
    }
}
```
By exploiting this function, the attacker can write arbitrary data into the periodAmountsWritten and periodNfpSecondsX96 mappings, potentially disrupting reward calculations and affecting other users.

## Recommended Mitigation Steps
Add the lock modifier to the cachePeriodEarned function to prevent unauthorized access and reentrancy attacks:
```Solidity
    /// @dev used by getReward() and saves gas by saving states
    function cachePeriodEarned(
        uint256 period,
        address token,
        address owner,
        uint256 index,
        int24 tickLower,
        int24 tickUpper,
        bool caching
    ) public override lock returns (uint256 amount) {
        // Function body remains the same
    }
```
### References
```Solidity
https://github.com/sherlock-au