xanoutas/solidity-security-findings huggingface dataset:
stealing rewards in AutoPxGlp and AutoPxGmx contract because function compound() allows user to set slippage for contract funds swaps
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L197-L296
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L230-L313


# Vulnerability details

## Impact
Function `compound()` in the `AutoPxGlp` and `AutoPxGmx` contracts is claiming all the rewards of the pool then swaps them if required and then deposits them in the `PirexGmx` again to compound the reward. when swapping called, code uses the slippage tolerance values that user specified This creates an issue where a single user specify slippage for all the pool contract rewards which belongs to all of the users. attacker can use this to steal contract rewards by creating a smart contract that first manipulate the pool in Uniswap (or any pool that GMX or Pirex protocol uses in depositing) then call `compound()` with very high slippage allowance (set `amountOutMinimum` very low) and code would swap rewards token with very bad price and then attacker contract would perform the reverse transaction in Uniswap.

## Proof of Concept
This is `compound()` code in `AutoPxGmx` contract:
```
    function compound(
        uint24 fee,
        uint256 amountOutMinimum,
        uint160 sqrtPriceLimitX96,
        bool optOutIncentive
    )
        public
        returns (
            uint256 gmxBaseRewardAmountIn,
            uint256 gmxAmountOut,
            uint256 pxGmxMintAmount,
            uint256 totalFee,
            uint256 incentive
        )
    {
        if (fee == 0) revert InvalidParam();
        if (amountOutMinimum == 0) revert InvalidParam();

        uint256 assetsBeforeClaim = asset.balanceOf(address(this));

        PirexRewards(rewardsModule).claim(asset, address(this));

        // Swap entire reward balance for GMX
        gmxBaseRewardAmountIn = gmxBaseReward.balanceOf(address(this));

        if (gmxBaseRewardAmountIn != 0) {
            gmxAmountOut = SWAP_ROUTER.exactInputSingle(
                IV3SwapRouter.ExactInputSingleParams({
                    tokenIn: address(gmxBaseReward),
                    tokenOut: address(gmx),
                    fee: fee,
                    recipient: address(this),
                    amountIn: gmxBaseRewardAmountIn,
                    amountOutMinimum: amountOutMinimum,
                    sqrtPriceLimitX96: sqrtPriceLimitX96
                })
            );

            // Deposit entire GMX balance for pxGMX, increasing the asset/share amount
            (, pxGmxMintAmount, ) = PirexGmx(platform).depositGmx(
                gmx.balanceOf(address(this)),
                address(this)
            );
        }
....
....
```
As you can see contract swaps the `gmxBaseReward` balance of contract for `gmx` token by calling `IV3SwapRouter` and the slippage parameters for swaps `amountOutMinimum` and `sqrtPriceLimitX96` are set by the values user provided. This would give attacker to p
Public/external functions on upgradeable contract should be virtual
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L107
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L133
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L151
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L179
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L207
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L229
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L242
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L257
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L270
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L281
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L338
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L373
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L432
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L461


# Vulnerability details

## Impact

To make potential upgrades of the `PirexRewards` contract possible, consider declaring all public and external functions `virtual`. This will make it possible to update logic in the future when inheriting from previous implementations.

## Proof of Concept
Say you wanted to introduce `PirexRewardsV2.sol` and override the existing `harvest` function:
```
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {PirexRewards} from "./PirexRewards.sol";

contract PirexRewardsV2 is PirexRewards {
    function harvest()
        public
        override
        returns (
            ERC20[] memory _producerTokens,
            ERC20[] memory rewardTokens,
            uint256[] memory rewardAmounts
        )
    {
        // Do something else
    }
}

```
This will not compile and instead result in the following error:
```
Error: 
Compiler run failed
error[4334]: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?
   --> src/PirexRewards.sol:338:5:
    |
338 |     function harvest()
    |     ^ (Relevant source part starts here and spans across multiple lines).
Note: Overriding function is here:
 --> src/PirexRewardsV2.sol:8:5:
  |
8 |     function harvest()
  |     ^ (Relevant source part starts here and spans across multiple lines).
```

## Tools Used
Manual audit.

## Recommended Mitigation Steps
The rule here should be to ensure all functions on the `PirexRewards` implementation are virtual, and all updates in future implementations use `virtual override`.
malicous caller and break reward accumilation
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Due to the lack of access control it is possible for a malicous actor to reset a users reward acrrual, meaning that when a user tried to claim said rewards they could be less than anticipated without the user knowing why or how

## Proof of Concept


    function userAccrue(ERC20 producerToken, address user) public {
        if (address(producerToken) == address(0)) revert ZeroAddress();
        if (user == address(0)) revert ZeroAddress();

        UserState storage u = producerTokens[producerToken].userStates[user];
        uint256 balance = producerToken.balanceOf(user);

        // Calculate the amount of rewards accrued by the user up to this call
        uint256 rewards = u.rewards +
            u.lastBalance *
            (block.timestamp - u.lastUpdate);

        u.lastUpdate = block.timestamp.safeCastTo32();
        u.lastBalance = balance.safeCastTo224();
        u.rewards = rewards;

        emit UserAccrue(producerToken, user, block.timestamp, balance, rewards);
    }

The above function can be called currently by anyone, users address is an arbiturary value the currently anyone can enter, if a malicious character has the address of a user who is interacting with this protocol, due to the following calculations 

u.rewards +
            u.lastBalance *
            (block.timestamp - u.lastUpdate);

A malicious actor can call this function on behalf of a given user and reset the reward accrual by reseting the u.lastUpdate and therefor restarting a reward accrual proccess. This particular user now has less u.rewards than they should
## Tools Used

## Recommended Mitigation Steps
To mitigate add some kind of access control to require(user == msg.sender)
Too small `FEE_DENOMINATOR` causes high fees in `AutoPxGmx` and `AutoPxGlp`
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
With the fee denominator (10,000) and the default fee values, users of the pools will have to pay unreasonably high fees.
## Proof of Concept
`FEE_DENOMINATOR` is a constant that defines the denominator that's used in fees calculation, it's set to `10000` in `AutoPxGmx` and `AutoPxGlp` ([AutoPxGmx.sol#L22](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L22), [AutoPxGlp.sol#L20](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L20)). With this denominator, fees paid by users of the pools seem too high:
1. Withdrawal fee is `300 / 10000 = 0.03 = 3%` ([AutoPxGmx.sol#L28](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L28), [AutoPxGlp.sol#L24](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L24)). If a whale withdraws \$1,000,000 worth of assets, they'll pay \$30,000 in withdrawal fees ([AutoPxGmx.sol#L173-L217](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L173-L217), [AutoPxGlp.sol#L151-L195](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L151-L195)).
1. Platform fee is `1000 / 10000 = 0.1 = 10%` ([AutoPxGmx.sol#L29](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L29), [AutoPxGlp.sol#L25](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L25)). Platform fee is paid from compounded rewards ([AutoPxGmx.sol#L289-L292](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L289-L292), [AutoPxGlp.sol#L252-L255](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L252-L255)), which means that 10% of rewards will go to the team/DAO/treasury.
1. Compound incentive is also `1000 / 10000 = 0.1 = 10%` ([AutoPxGmx.sol#L30](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L30), [AutoPxGlp.sol#L26](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L26)). This incentive is enabled optionally by a caller of the `compound` function ([AutoPxGmx.sol#L246](https://github.com/code-
WETH reward can be stolen during compounding
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Swapping WETH for GMX during compounding in the `AutoPxGmx` pool can be manipulated: an attacker can use a pool with a manipulated WETH/GMX price and/or lower TVL to buy WETH from `AutoPxGmx` at a cheaper price, basically stealing a part of rewards from the vault.
## Proof of Concept
`AutoPxGmx` implements the `compound` function to claim rewards (WETH and pxGMX) from the underlying pxGMX pool, swap WETH for GMX tokens on Uniswap, and stake pxGMX and GMX tokens ([AutoPxGmx.sol#L242](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L242)). Every Uniswap V3 pool is identified by three parameters: first token's address, second token's address, and the swap fee amount (see the salt here: [UniswapV3PoolDeployer.sol#L35](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3PoolDeployer.sol#L35)). The `compound` function, while hard coding token addresses ([AutoPxGmx.sol#L270-L271](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L270-L271)), allows the caller to specify a swap fee ([AutoPxGmx.sol#L243](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L243)), allowing a caller to choose any of the deployed WETH/GMX pools. At the moment of the audit, there are three WETH/GMX pools on Arbitrum:
1. [with 1% swap fees](https://info.uniswap.org/#/arbitrum/pools/0x80a9ae39310abf666a87c743d6ebbd0e8c42158e), the official one, has the highest TVL of $17.03m;
1. [with 0.3% swap fees](https://info.uniswap.org/#/arbitrum/pools/0x1aeedd3727a6431b8f070c0afaa81cc74f273882), TVL $4.06m;
1. [with 0.05% swap fees](https://arbiscan.io/address/0xb435ebfE0BF4CE66810AA4d44e3a5CA875D40DB1), TVL $603.49.

(More pools can be deployed if new fee tiers are enabled in the [Uniswap V3 Factory](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Factory.sol#L61-L72) contract).

An attacker can use the smallest of the pools by TVL to perform the swap during compounding, which allows atomic sandwich attacks:
1. an attacker buys GMX from the 0.05% pool to increase its price in terms of WETH;
1. the attacker calls the `compound` function and sets: `fee` to 500 to use the 0.05% pool; `amountOutMinimum` to 1 to bypass the non-zero slippage tolerance check and still have ~100% slippage; `sqrtPriceLimitX96` to 0 to swap the entire amount;
1. in the `compound` call, `AutoPxGmx` buys GMX and sells WETH at an increased price, getting less GMX and selling more WETH than expected; this pushes the price of GMX a little higher;
1. the attacker sells their GMX at the higher price, getting more WETH as a profit.

However, since compounding is done before depositing ([AutoPxGmx
Reward tokens mismanagement can cause users losing rewards
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
A user (which can also be one of the autocompounding contracts, `AutoPxGlp` or `AutoPxGmx`) can loss a reward as a result of reward tokens mismanagement by the owner.
## Proof of Concept
The protocol defines a short list of reward tokens that are hard coded in the `claimRewards` function of the `PirexGmx` contract ([PirexGmx.sol#L756-L759](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L756-L759)):
```solidity
rewardTokens[0] = gmxBaseReward;
rewardTokens[1] = gmxBaseReward;
rewardTokens[2] = ERC20(pxGmx); // esGMX rewards distributed as pxGMX
rewardTokens[3] = ERC20(pxGmx);
```

The fact that these addresses are hard coded means that no other reward tokens will be supported by the protocol. However, the `PirexRewards` contract maintains a different list of reward tokens, one per producer token ([PirexRewards.sol#L19-L31](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L19-L31)):
```solidity
struct ProducerToken {
    ERC20[] rewardTokens;
    GlobalState globalState;
    mapping(address => UserState) userStates;
    mapping(ERC20 => uint256) rewardStates;
    mapping(address => mapping(ERC20 => address)) rewardRecipients;
}

// Producer tokens mapped to their data
mapping(ERC20 => ProducerToken) public producerTokens;
```

These reward tokens can be added ([PirexRewards.sol#L151](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L151)) or removed ([PirexRewards.sol#L179](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L179)) by the owner, which creates the possibility of a mismanagement:
1. the owner can mistakenly remove one of the reward tokens hard coded in the `PirexGmx` contract;
1. the owner can add reward tokens that are not supported by the `PirexGmx` contract.

Such mismanagement can cause users to lose rewards for two reasons:
1. reward state of a user is updated *before* their rewards are claimed;
1. it's the reward token addresses set by the owner of the `PirexRewards` contract that are used to transfer rewards.

In the `claim` function:
1. `harvest` is called to pull rewards from GMX ([PirexRewards.sol#L377](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L377)):
    ```solidity
    harvest();
    ```
1. `claimReward` is called on `PirexGmx` to pull rewards from GMX and get the hard coded lists of producer tokens, reward tokens, and amounts ([PirexRewards.sol#L346-L347](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L346-L347)):
GMX token approval is not removed from old `stakedGmxTracker`
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L277


# Vulnerability details

## Impact
An old `stakedGmxTracker` contract can withdraw all GMX tokens held by `PirexGmx`.
## Proof of Concept
The `configureGmxState` function allows to update addresses of all GMX contracts in one call ([PirexGmx.sol#L272](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L272)):
```solidity
function configureGmxState() external onlyOwner whenPaused {
    // Variables which can be assigned by reading previously-set GMX contracts
    rewardTrackerGmx = RewardTracker(gmxRewardRouterV2.feeGmxTracker());
    rewardTrackerGlp = RewardTracker(gmxRewardRouterV2.feeGlpTracker());
    feeStakedGlp = RewardTracker(gmxRewardRouterV2.stakedGlpTracker());
    stakedGmx = RewardTracker(gmxRewardRouterV2.stakedGmxTracker());
    glpManager = gmxRewardRouterV2.glpManager();
    gmxVault = IVault(IGlpManager(glpManager).vault());

    emit ConfigureGmxState(
        msg.sender,
        rewardTrackerGmx,
        rewardTrackerGlp,
        feeStakedGlp,
        stakedGmx,
        glpManager,
        gmxVault
    );

    // Approve GMX to enable staking
    gmx.safeApprove(address(stakedGmx), type(uint256).max);
}
```

The function also approves spending of the unlimited amount of GMX tokens to the `stakedGmx` address, which is the `RewardTracker` contract for staked GMX. However, the previously given approval is not removed before the `stakedGmx` address is updated, which allows the old `stakedGmxTracker` contract to withdraw GMX tokens of `PirexGmx`.

The `RewardTracker` contract allows anyone to stake GMX tokens from an arbitrary address that has previously approved the contract:
- [RewardTracker.sol#L105-L108](https://github.com/gmx-io/gmx-contracts/blob/65e62b62aadea5baca48b8157acb9351249dbaf1/contracts/staking/RewardTracker.sol#L105-L108):
  ```solidity
  function stakeForAccount(address _fundingAccount, address _account, address _depositToken, uint256 _amount) external override nonReentrant {
      _validateHandler();
      _stake(_fundingAccount, _account, _depositToken, _amount);
  }
  ```
- [RewardTracker.sol#L237-L250](https://github.com/gmx-io/gmx-contracts/blob/65e62b62aadea5baca48b8157acb9351249dbaf1/contracts/staking/RewardTracker.sol#L237-L250):
    ```solidity
    function _stake(address _fundingAccount, address _account, address _depositToken, uint256 _amount) private {
        require(_amount > 0, "RewardTracker: invalid _amount");
        require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken");

        IERC20(_depositToken).safeTransferFrom(_fundingAccount, address(this), _amount);

        _updateRewards(_account);

        stakedAmounts[_account] = stakedAmounts[_account].add(_amount);
        depositBalances[_account][_depositToken] = depositBalances[_account][_depositToken].add(_amount);
Simultaneous use of the same address for “treasury”  and “contributors” 
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

  https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexFees.sol#L50


# Vulnerability details


1. Title: Simultaneous use of the same address for “treasury”  and “contributors” 
2. Details:   Although the probability of this happening is very low, it is possible that the same address will be set for both reward recipients.
3. GitHub Permalinks:
  https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexFees.sol#L50

4. Mitigation risk:   Add the following line in the  constructor function:
  Require (_treasury !=  _contributors, “Same address”)

5. Difficulty+Impact: OWASP ratings: Medium   (likelihood:  Low  - impact:  high)
Missing status condition check
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details


1. Title:  Missing status condition check.
2. Details:   If the “status”   variable  (declared in line:  243)  has a false value, it means that no transfer has been made. And the transferring has not been executed for various reasons, so it is logical that no changes should be made in the state of the contract, functions and variables. 
3. GitHub Permalinks: 
 https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/PirexERC4626.sol#L263

4. Mitigation risk:  
Therefore, a conditional comparison command must be added to the execution of the “        afterTransfer(from, to, amount);” function as described below.

5. Difficulty+Impact: OWASP ratings:  medium (likelihood:  medium - impact: medium)