xanoutas/solidity-security-findings huggingface dataset:
`PirexERC4626` and `PirexGmx` doesn't support erc20 tokens with fee on transfer 
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/PirexGmx.sol#L436


# Vulnerability details

# `PirexERC4626` and `PirexGmx` doesn't support erc20 tokens with fee on transfer 
### Vulnerability details
There are ERC20 tokens that charge fee for every `transfer()` / `transferFrom()`.

Different functions assumes assumes that the received amount is the same as the transfer amount, and uses it to calculate attributions, balance amounts, etc. While the actual transferred amount can be lower for those tokens.
### Github Permalinks
https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/PirexGmx.sol#L436
        stakedGlp.transferFrom(msg.sender, address(this), amount);

https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/vaults/PirexERC4626.sol#L243
        bool status = ERC20.transfer(to, amount);

https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/vaults/PirexERC4626.sol#L261
        bool status = ERC20.transferFrom(from, to, amount);

### Recommendation
Consider comparing before and after balance to get the actual transferred amount.
Unbounded loop can block `claim`
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/PirexRewards.sol#L396-L415
https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/PirexGmx.sol#L824


# Vulnerability details


## Unbounded loop can block `claim`
### Impact
There are no bounds on the number of rewardTokens in the loop, this can run out of gas due to cost of the operations.
### Proof Of Concept
https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/PirexRewards.sol#L396-L415
```
  function claim(ERC20 producerToken, address user) external {
        
    {...}
           uint256 rLen = rewardTokens.length;
            // Transfer the proportionate reward token amounts to the recipient
            for (uint256 i; i < rLen; ++i) {
                ERC20 rewardToken = rewardTokens[i];
                address rewardRecipient = p.rewardRecipients[user][rewardToken];
                address recipient = rewardRecipient != address(0)
                    ? rewardRecipient
                    : user;
                uint256 rewardState = p.rewardStates[rewardToken];
                uint256 amount = (rewardState * userRewards) / globalRewards;

                if (amount != 0) {
                    // Update reward state (i.e. amount) to reflect reward tokens transferred out
                    p.rewardStates[rewardToken] = rewardState - amount;

                    producer.claimUserReward(//@audit expensive operations
                        address(rewardToken),
                        amount,
                        recipient
                    );
                }
            }
        }
```
https://github.com/code-423n4/2022-11-redactedcartel/blob/684627b7889e34ba7799e50074d138361f0f532b/src/PirexGmx.sol#L824
```
    function claimUserReward(
        address token,
        uint256 amount,
        address receiver
    ) external onlyPirexRewards {
        if (token == address(0)) revert ZeroAddress();
        if (amount == 0) revert ZeroAmount();
        if (receiver == address(0)) revert ZeroAddress();

        (uint256 postFeeAmount, uint256 feeAmount) = _computeAssetAmounts(
            Fees.Reward,
            amount
        );

        if (token == address(pxGmx)) {
            // Mint pxGMX for the user - the analog for esGMX rewards
            pxGmx.mint(receiver, postFeeAmount);

            if (feeAmount != 0) pxGmx.mint(address(pirexFees), feeAmount);
        } else if (token == address(gmxBaseReward)) {
            gmxBaseReward.safeTransfer(receiver, postFeeAmount);

            if (feeAmount != 0)
                gmxBaseReward.safeTransfer(address(pirexFees), feeAmount);
        }

        emit ClaimUserReward(receiver, token, amount, postFeeAmount, feeAmount);
    }
```

### Recommended Mitigation Steps
Have an upper bound on the number of orders to avoid this issue
`compound` could be used by uniswap stakers to maximize fees for AutoPxGmx users
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L243


# Vulnerability details

## Impact
Anyone can call `AutoPxGmx::compound`. Hence a staker in the `10000` (1% fee) uniswap pool can call compound with that pool and take a larger fee from AutoPxGmx users maximizing their gains and griefing users.

## Proof of Concept

`fee` chooses which uniswap pool to use for trading reward `weth` for `gmx`, which is then used for more staking:

`vaults/AutoPxGmx.sol`:
```javascript
242:    function compound(
243:        uint24 fee,
244:        uint256 amountOutMinimum,
245:        uint160 sqrtPriceLimitX96,
246:        bool optOutIncentive
247:    )
```

Since `fee` is a parameter that the caller chooses a staker in the 1% fee `gmx-weth` uniswap pool can front run or just periodically call `AutoPxGmx::compound` to maximize their gains and also grief AutoPxGmx users.

## Tools Used
vs code

## Recommended Mitigation Steps
Use the fee `poolFee` which is used when `compound` is automatically called from other functions in the contract.
Compounding is vulnerable to sandwich attack
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L275


# Vulnerability details

## Impact

Function `compound()` is called in every deposit/withdraw in `AutoPxGmx` contract. It claim rewards in form of `gmxBaseReward` and swap them to `gmx` token. They used `amountOutMinimum` from input params 
```solidity
gmxAmountOut = SWAP_ROUTER.exactInputSingle(
    IV3SwapRouter.ExactInputSingleParams({
        tokenIn: address(gmxBaseReward),
        tokenOut: address(gmx),
        fee: fee,
        recipient: address(this),
        amountIn: gmxBaseRewardAmountIn,
        amountOutMinimum: amountOutMinimum,
        sqrtPriceLimitX96: sqrtPriceLimitX96
    })
);
```

However `amountOutMinimum` always has the value `1` when called by `withdraw()` or `deposit()` function.
```solidity
function beforeDeposit(
    address,
    uint256,
    uint256
) internal override {
    compound(poolFee, 1, 0, true);
}
```

An attacker can front-run swap in Router before `compound()` to make a profit. 

## Proof of Concept

Please refer to [yDai Incident](https://peckshield.medium.com/the-ydai-incident-analysis-forced-investment-2b8ac6058eb5) to check the severity of a harvest function without slippage control.

Please refer to [Mushrooms-finance-theft](https://medium.com/immunefi/mushrooms-finance-theft-of-yield-bug-fix-postmortem-16bd6961388f) to check how likely this kind of attack might happen.


## Recommended Mitigation Steps

Consider adding using some fixed lowerbound rate (since it's trading from pxGMX to GMX so the rate should not be volatile)
DoS on claiming rewards in `PirexRewards` is possible
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/9e9bb60f117334da7c5d851646a168ca271575fc/src/PirexRewards.sol#L373


# Vulnerability details

### Proof of Concept

The `claim` method in `PirexRewards` iterates over the `rewardTokens` array for a `producerToken`. Now this array is completely managed by the contract’s `owner` who can call `addRewardToken` which pushes a new value in that array, as many times as he decides with whatever value he decides.

Let’s look at the following scenario:

1. Owner has turned malicious or is compromised
2. Owner calls `addRewardToken` a huge amount of times, which results in the `rewardTokens` array being huge
3. Now when a user calls `claim` the code will take a crazy amount of gas, because it has to iterate over the whole `rewardTokens` array. Since the amount of gas will possibly be more than the block gas limit, this results in a DoS and the user won’t be able to claim.

Or this scenario:

1. Owner has turned malicious or is compromised
2. Owner calls `addRewardToken` with an address that is not really a token
3. Now when a user calls `claim` the code will try to do a call to `producer.claimUserReward` giving the address of the `rewardToken`, but since it is not really a token (or maybe it is just a random address with no bytecode) the call will revert. Actually it will always revert, which is a DoS state.

### Impact

This is a centralisation vulnerability allowing the owner to stop the user rewards anytime. Since it requires a malicious/compromised owner it is Medium severity

### Recommendation

Add a way for the user to claim rewards only from a token he chooses, not to have to go through all reward tokens on each claim.
Slippage protection on `AutoPxGmx::compound` calls is insufficient and can result in MEV
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/9e9bb60f117334da7c5d851646a168ca271575fc/src/vaults/AutoPxGmx.sol#L242


# Vulnerability details

### Proof of Concept

The `compound` method has the `amountOutMinimum` parameter, which basically serves as the slippage tolerance parameter. The problem is that everywhere in the code where `compound` is called, the value of `amountOutMinimum` is just 1 wei, which basically means all swaps executed in the `compound` method can be sandwiched and the user can lose a huge amount of value due to slippage.

### Impact

This vulnerability can easily be exploited by a MEV bot that is scouting the public mempool. Setting severity to Medium because that’s what MEV attacks usually get rated as.

### Recommendation

Add a setter to dynamically configure slippage tolerance amounts on-chain, or always make sure all transactions go through a private/dark mempool
Potential PirexReward's producerTokens's rewardToken unsynced with PirexGmx rewardToken can miss calculate the actual reward for user
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L31
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L151-L197


# Vulnerability details

## Impact
Potential PirexReward's producerTokens's rewardToken unsynced with PirexGmx rewardToken can miss calculate the actual reward for user

## Proof of Concept

PirexReward initialization does not include rewardToken initialization for producerTokens. Meanwhile inside PirexGmx, it is already hardcoded, the `claimRewards` will return array of rewardToken contains 4 exact items.

In order for PirexReward to `sync` or know what are the rewardToken with its producerTokens, there are two functions related to this `addRewardToken` and `removeRewardToken`, (also this is prone to issues)

If at some point user do `harvest()` but the PirexReward only insert 2 rewardToken, or maybe insert 4 rewardToken but one of it is not the correct producerToken, this can raise an issue. 

User'ss reward is not being correctly calculated since the rewardToken is missing, and the accounting is not running correctly (until the Owner/Admin correctly input the rewardToken synced with PirexGmx's)

```solidity
File: PirexRewards.sol
338:     function harvest()
339:         public
340:         returns (
341:             ERC20[] memory _producerTokens,
342:             ERC20[] memory rewardTokens,
343:             uint256[] memory rewardAmounts
344:         )
345:     {
346:         (_producerTokens, rewardTokens, rewardAmounts) = producer
347:             .claimRewards();
348:         uint256 pLen = _producerTokens.length;
349: 
350:         // Iterate over the producer tokens and update reward state
351:         for (uint256 i; i < pLen; ++i) {
352:             ERC20 p = _producerTokens[i];
353:             uint256 r = rewardAmounts[i];
354: 
355:             // Update global reward accrual state and associate with the update of reward state
356:             ProducerToken storage producerState = producerTokens[p];
357: 
358:             _globalAccrue(producerState.globalState, p);
359: 
360:             if (r != 0) {
361:                 producerState.rewardStates[rewardTokens[i]] += r;
362:             }
363:         }
364: 
365:         emit Harvest(_producerTokens, rewardTokens, rewardAmounts);
366:     }
367: 
```

from this `harvest()` function the `ProducerToken storage producerState = producerTokens[p];` is not being checked if it exist or not if the token is already being inputed by Admin/Owner.

If user assign to get reward of token X, meanwhile there is no rewardToken X set (or wrongly set) inside PirexRewards the harvest() will skip the user reward accounting, thus decrease the actual reward user should get. (because of delayed userAccrue/globalAccrue, for the token X)

## Tools Used

Manual

## Recommended Mitigation Steps

Include pirexReward initialization of rewardToken inside initialization function, or check if produceToken (and its
Uint underflow issue
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGlp.sol#L191


# Vulnerability details

## Impact
Potential underflow if `shares` is greater than `_totalSupply`
## Proof of Concept
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGlp.sol#L191
## Tools Used
Manual review
## Recommended Mitigation Steps
Before the following line, check if `_totalSupply` is greater or equal to `shares`
Potential uint underflow
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGlp.sol#L163


# Vulnerability details

## Impact
Potential underflow if `shares` is greater than `_totalSupply`
## Proof of Concept
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGlp.sol#L163
## Tools Used
Manual review
## Recommended Mitigation Steps

Before the following line, check if `_totalSupply` is greater or equal to `shares`
Allowance underflow
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/PirexERC4626.sol#L132


# Vulnerability details

## Impact
I think the following check is unnecessary but furthermore, there should be a comparison if the allowance amount `allowed` is equal or greater than `shares` amount. Otherwise, we would end up with underflow of the uint value

## Proof of Concept
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/PirexERC4626.sol#L132
## Tools Used
Manual review
## Recommended Mitigation Steps

I suppose the condition check should be `if (allowed - shares >= 0)
Wrong comparison
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/PirexERC4626.sol#L109


# Vulnerability details

## Impact
I think the following check is unnecessary but furthermore, there should be a comparison if the allowance amount `allowed` is equal or greater than `shares` amount. Otherwise, we would end up with underflow of the uint value

## Proof of Concept
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/PirexERC4626.sol#L109
## Tools Used
Manual review
## Recommended Mitigation Steps

I suppose the condition check should be `if (allowed - shares >= 0)
Use Of block.timestamp Can Result In Attacker Manipulating His/Her Rewards In Their Favour
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L291


# Vulnerability details

## Impact
If an attacker manipulates the block.timestamp in their favor then they can get higher rewards as `uint256 rewards = u.rewards +
            u.lastBalance *
            (block.timestamp - u.lastUpdate)` in this equation lets say the attacker called this just now , and on the next second the value
of block.timestamp would be slightly higher, but manipulating the block.timestamp might result in higher rewards for the attacker.

## Proof of Concept
1.) Bob calls userAccrue https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L291 and now his lastUpdate value is the current time
2.) Now after one second when the value of block.timestamp should be one second higher , Bob manipulates the value of block.timestamp to be , lets
say 15 seconds  lesser(so that `block.timestamp - u.lastUpdate` value becomes larger) and now Bob accrues more rewards than he should .

## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Consider making an internal function which returns the value of block.timestamp  , and the userAccrue function uses its value instead of using the 
value of block.timestamp directly. Consider them as 2 different transactions.
Lack Of Proper Access Control Might Lead To User Getting Lesser Rewards
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L281-L295


# Vulnerability details

## Impact
We can call the function userAccrue for some other user and make their rewards lesser then they expect.
In the function https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L281-L295 it calculates the rewards 
for a user that are being accrued over a period of time. The math to calculate how much reward a user has rewards accrued is 
`uint256 rewards = u.rewards +
            u.lastBalance *
            (block.timestamp - u.lastUpdate);`
We can see from this equation that more the value of `block.timestamp - u.lastUpdate` more will be the rewards accrued.


## Proof of Concept
1.) Bob decides to stake his producer token for 2 weeks (which would make his lastUpdate value after two weeks as two weeks) to get higher rewards
2.) Alice comes along  , and after one week calls the function userAccrue with the same producer token and address of user bob in the user parameter.
3.) Now the lastUpdate for the user Bob after 2 weeks is not two weeks but one week because Alice called userAccrue on behalf of Bob after one week, and now Bob will get reward for one week instead of 2.


## Tools Used
Manual analysis

## Recommended Mitigation Steps
Most simple mitigation would be to add a check something like `require(user == msg.sender)`  this way the `user` has to be the user himself who calls the function.
Add reward token existence check in order to avoid user reward lost.
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L386-L387


# Vulnerability details

## Impact

The user can lost his rewards if the reward token is removed from the ```producerTokens[producerToken].rewardTokens``` list. If the reward token is removed, the rewardToken length is [going to be zero](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L387), the user rewards [going to be zero](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L391) and the [for statement will never enter](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L396) so the user will lost his rewards for this token and the [global state rewards](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L390) will save inaccurate information.

## Proof of Concept

1. Add user pxGMX token an accrue his rewards
2. Remove rewardToken
3. The user won't get his rewards even if the reward token is added again

I did a test for this situation:
```solidity
// test/PirexRewards.t.sol
function testClaimRewardsWithRemovedToken(
    uint32 secondsElapsed,
    uint8 multiplier,
    bool useETH
) external {
    vm.assume(secondsElapsed > 10);
    vm.assume(secondsElapsed < 365 days);
    vm.assume(multiplier != 0);
    vm.assume(multiplier < 10);
    address userX = testAccounts[0];
    uint256 gmxAmount = 100e18;

    _mintApproveGmx(gmxAmount, address(this), address(pirexGmx), gmxAmount);
    pirexGmx.depositGmx(gmxAmount, userX);

    assertEq(weth.balanceOf(userX), 0);

    vm.warp(block.timestamp + secondsElapsed);

    //
    // Add reward token and harvest rewards from Pirex contract
    //
    pirexRewards.addRewardToken(pxGmx, weth);
    pirexRewards.harvest();

    pirexRewards.userAccrue(pxGmx, userX);
    (, , uint256 globalRewardsBeforeClaimPxGmx) = _getGlobalState(
        pxGmx
    );
    (, , uint256 userRewardsBeforeClaimPxGmx) = pirexRewards
        .getUserState(pxGmx, userX);
    //
    // Sum of reward amounts that the user is entitled to
    //
    uint256 expectedClaimAmount = ((pirexRewards.getRewardState(
        pxGmx,
        weth
    ) * _calculateUserRewards(pxGmx, userX)) /
        _calculateGlobalRewards(pxGmx));

    //
    // Remove WETH from pxGMX
    //
    vm.expectEmit(true, false, false, true, address(pirexRewards));
    emit RemoveRewardToken(pxGmx, 0);
    pirexRewards.removeRewardToken(pxGmx, 0);

    // vm.expectEmit(true, false, false, false, address(pirexGmx));
    // emit ClaimUserReward(userX, address(0), 0, 0, 0);  // this emit is not called
    vm.expectEmit(true, true, false, true, address(pirexRewards));
    emit Claim(pxGmx, userX);
    pirexRewards.claim(pxGmx, userX);

    (, , uint25