xanoutas/solidity-security-findings huggingface dataset:
The compound() function is used with a fixed amountOutMinimum value
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

The ```compound()``` function helps to swap the gmxBaseReward (ether) for GMX tokens then the GMX tokens are deposited for pxGMX. In the swap function the ```amountOutMinimum``` helps to put the minimum value that is expected for the swap. The [fixed amountOutMInimum](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol) assumes that the price can not be manipulated by different techniques so it is better to calculate the minimum accepted amount.

The ```amountOutMinimum``` should be calculated with the Uniswap SDK or an onchain price oracle as their [documentation](https://docs.uniswap.org/contracts/v3/guides/swaps/single-swaps) says:

```
For a real deployment, this value should be calculated using our SDK or an onchain price oracle - this helps protect against getting an unusually bad price for a trade due to a front running sandwich or another type of price manipulation.
```

## Proof of Concept

The following lines use a fixed amountOutMinimum value:
- [src/vaults/AutoPxGmx.sol#L227](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L227)
- [src/vaults/AutoPxGmx.sol#L321](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L321)
- [src/vaults/AutoPxGmx.sol#L345](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L345)

## Tools used
VisualStudio

## Recommended Mitigation Steps

Calculates the ```amountOutMinimum``` using the Uniswap SDK, an onchain price oracle or a time weighted average price.
A malicious user might call `AutoPxGmx.compound()` with a higher fee than `poolFee`.
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
A malicious user might call `AutoPxGmx.compound()` with a higher fee than `poolFee`.

As a result, there would be a fund loss for the users because they paid more than expected for the swap router.

## Proof of Concept
As we can see [here](https://docs.uniswap.org/concepts/protocol/fees#pool-fees-tiers), Uniswap V3 introduces multiple pools for each token pair.

In the `AutoPxGmx.sol`, all functions like [withdraw()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L321), [redeem()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L345) and [beforeDeposit()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L227) use the `poolFee` set by admin.

But `compound()` itself is a public function and users can call this function for the incentive.

Users can set the `fee` as a parameter and the below situation would be possible.

- Admin set `poolFee = 0.3%` and all functions mentioned above will use this fee percent.
- A malicious user calls `compound()` with `fee = 1%`.
- Then the pool of `fee = 1%` might exist [here](https://arbiscan.io/address/0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45#code#F6#L37), the swap router will take 1% as a fee.
- As a result, the users get fewer `gmx` than expected.

## Tools Used
Manual Review

## Recommended Mitigation Steps
I think we should remove the `fee` param from `compound()` function and use `poolFee` by default.
`platform` should be approved again after it's changed by the admin.
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In `AutoPxGmx.sol` and `AudotPxGlp.sol`, it doesn't approve properly when `platform` is changed.

As a result, `PirexGmx` contract can't transfer `gmx` or `gmxBaseReward` from these contracts and the main logic won't work as expected.

## Proof of Concept
As we can see [here](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L97) and [here](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L87), it approves the `platform` in the constructor.

But in the [AutoPxGmx.setPlatform()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L152-L158) and [AudotPxGlp.setPlatform()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L130-L136), it doesn't approve again for the updated `platform`.

As a result, [AutoPxGmx.depositGmx()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L384-L388) and [AudotPxGlp.depositGlp()](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L389-L400) wouldn't work properly and always revert.

```solidity
    // Convert sender GMX into pxGMX and get the post-fee amount (i.e. assets)
    (, uint256 assets, ) = PirexGmx(platform).depositGmx(
        amount,
        address(this)
    );
```

```solidity
    // Approve as needed here since it can be a new whitelisted token (unless it's the baseReward)
    if (erc20Token != gmxBaseReward) {
        erc20Token.safeApprove(platform, tokenAmount);
    }

    (, uint256 assets, ) = PirexGmx(platform).depositGlp(
        token,
        tokenAmount,
        minUsdg,
        minGlp,
        address(this)
    );
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
We should approve the new `platform` again after it's changed.
`AutoPxGmx.compound` function can be directly called with a `fee` input value that is not the configured Uniswap pool fee
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L315-L337
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L339-L362
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L242-L313


# Vulnerability details

## Impact
Calling the following `AutoPxGmx.withdraw` and `AutoPxGmx.redeem` functions would execute `compound(poolFee, 1, 0, true)`, which uses the configured Uniswap pool fee as the `fee` input of the `AutoPxGmx.compound` function below to further call the `SWAP_ROUTER.exactInputSingle` function. Yet, when calling the `AutoPxGmx.compound` function directly, it is possible that a pool fee, which is not the configured pool fee, is used. Using a pool corresponding to a pool fee that is not the configured pool fee can cause the increment of the asset/share amount resulted from calling the `AutoPxGmx.compound` function to be lower than that when using a pool corresponding to the configured pool fee; as a result, the withdrawn pxGMX amount resulted from calling functions like `AutoPxGmx.withdraw` and `AutoPxGmx.redeem` after the `AutoPxGmx.compound` function using a non-configured pool fee is directly called can be lower than that resulted from calling functions like `AutoPxGmx.withdraw` and `AutoPxGmx.redeem` without the `AutoPxGmx.compound` function being directly called beforehand.

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L315-L337
```solidity
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public override returns (uint256 shares) {
        // Compound rewards and ensure they are properly accounted for prior to withdrawal calculation
        compound(poolFee, 1, 0, true);

        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max)
                allowance[owner][msg.sender] = allowed - shares;
        }

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }
```

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L339-L362
```solidity
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public override returns (uint256 assets) {
        // Compound rewards and ensure they are properly accounted for prior to redemption calculation
        compound(poolFee, 1, 0, true);

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max)
                allowance[owner][msg.sender] = allowed - shares;
User can continuosly accrue rewards they are not due
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
It is possible that block.timestamp can be manipulted by a user, thus allowing a malicious user to continuously acrue rewards they are not due, as long as the value is not 0 then rewards will be accrued


    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 following math means that as long as block.timesamp - u.lastUpdate is >0 rewards will be accrued which can be withdrawn at any time and will be rewards this user is not due

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


## Tools Used
static code audit

## Recommended Mitigation Steps
Save the last timestamp to a mapping and do not allow it to be updated unless rewardClaimed == true, make users withdraw any rewards acrued before allowing an update to values which maybe used to acrue rewards or increase reward values as a matter of course.
Functions like `AutoPxGmx.withdraw` and `AutoPxGmx.redeem` do not provide effective slippage control
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L315-L337
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L339-L362
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L242-L313


# Vulnerability details

## Impact
As shown below, calling the `AutoPxGmx.withdraw` and `AutoPxGmx.redeem` functions would execute `compound(poolFee, 1, 0, true)`, which uses the hardcoded `1` as the `amountOutMinimum` input of the `AutoPxGmx.compound` function to further call the `SWAP_ROUTER.exactInputSingle` function. However, calling the `SWAP_ROUTER.exactInputSingle` function with `amountOutMinimum` being hardcoded to `1` does not provide an effective slippage control, which is unlike the case where the `AutoPxGmx.compound` function could be called directly with a specified `amountOutMinimum` that provides an effective slippage control. When the Uniswap V3 pool becomes relatively imbalanced comparing to when the pool is relatively balanced, executing `compound(poolFee, 1, 0, true)`, which does not provide an effective slippage control, can result in a much smaller minted pxGMX reward amount due to a much smaller GMX amount outputted after swapping the same input WETH reward amount; as a result, calling functions like `AutoPxGmx.withdraw` and `AutoPxGmx.redeem` in this situation can result in a much smaller withdrawn pxGMX amount.

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L315-L337
```solidity
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public override returns (uint256 shares) {
        // Compound rewards and ensure they are properly accounted for prior to withdrawal calculation
        compound(poolFee, 1, 0, true);

        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max)
                allowance[owner][msg.sender] = allowed - shares;
        }

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }
```

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L339-L362
```solidity
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public override returns (uint256 assets) {
        // Compound rewards and ensure they are properly accounted for prior to redemption calculation
        compound(poolFee, 1, 0, true);

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max)
                allowance[owner][msg.sende
Solmate saftransfer and safetransferfrom does not check the codesize of the token address, which may lead to fund loss
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L392-L946
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/PirexERC4626.sol#L71-L146


# Vulnerability details

In PirexGmx.sol and ERC4626Vault.sol, the contract uses the solmate library which does not check the existence of code at the token address. This is a known issue when using solmate's libraries. 

Reference: https://code4rena.com/reports/2022-08-olympus/#m-02-solmate-safetransfer-and-safetransferfrom-does-not-check-the-codesize-of-the-token-address-which-may-lead-to-fund-loss

## Impact

Possible loss of funds and miscalculation.

## Proof of Concept

If safetransfer() and safetransferfrom() are called on a token address that doesn't have a contract in it, it will always return success and bypass the return value check. The protocol will think that funds have been transferred and is successful, but in reality funds were never transferred.

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L392
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L506
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L643
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L844-L847
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexGmx.sol#L946
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/PirexERC4626.sol#L71-L146

## Tools Used

Manual Review

## Recommended Mitigation Steps

Use openzeppelin's safeERC20 or implement a code existence check.
Compromised contributors could still transfer fees
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexFees.sol#L100-L117


# Vulnerability details

## Impact
The probability that contributors will lose their private key and be compromised is not low, nor is there any guarantee they will not become malicious 
later on. Due to the fact that everyone can call ```distributeFees``` function, fees can still be transferred to that malicious address until the owner get notified and change the address.

```solidity 
    function distributeFees(ERC20 token) external {
        uint256 distribution = token.balanceOf(address(this));
        uint256 treasuryDistribution = (distribution * treasuryFeePercent) /
            FEE_PERCENT_DENOMINATOR;
        uint256 contributorsDistribution = distribution - treasuryDistribution;

        emit DistributeFees(
            token,
            distribution,
            treasuryDistribution,
            contributorsDistribution
        );

        // Favoring push over pull to reduce accounting complexity for different tokens
        token.safeTransfer(treasury, treasuryDistribution);
        token.safeTransfer(contributors, contributorsDistribution);
    }
}
```

## Proof of Concept
Take this scenario for example:

1- Contributors private key gets compromised.

2- By the time the owner gets notified (Which could take some time), he will need to call ```setFeeRecipient``` function in order to change the Contributors address and prevent the attacker from transferring fees.

3- The little period mention above will give the attacker enough time to call ```distributeFees``` function many times and steal 25% of every token the ```PirexFees``` contract is holding.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider adding ```onlyOwner``` modifer to prevent other parties from calling ```distributeFees``` function.
fn distribution feed need modifier to identify when it needed to send 
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Proof of Concept
This fn for distributionFees need modifier since it was used for notify if was special condition was used. 

## Recommended Mitigation Steps
by adding modifier, at example :

```
    modifier Fees {
        require(
            initialized,
            "FeeDistributor: Fees cannot be distributed until Distributor got fees."
        );
        _;
    }
```
and adding 

```
bool public initialized;

```

## Tools Used
manual review
Set the minimum amount for fn distributeFee()
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Proof of Concept
this condition can return if the balance was not fullfill `MINIMUM_AMOUNT`

by adding the condition down below :

```

        if (balance < MINIMUM_AMOUNT) {
            return;
        }
```

and set by : 

```
    uint private constant MINIMUM_AMOUNT = 1e8;
```

this `MINIMUM_AMOUNT` can handled if the ERC20 token can't exceeded the condition of another different tokens you wanted.


## Tool Used
manual Review
Initialize function in PirexRewards.sol can be front-run
Audit: 2022-11-redactedcartel-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
PirexRewards have a public initialize function that can be front-run, allowing an attacker to be owner of the contract and call functions protected by onlyOwner.

## Proof of Concept
Alice deploys the PirexRewards contract. Haxor detects the contract initialization and front-runs it calling `intialize()` function which sets his own address as the owner. As a result Haxor now has access to privileged functions and can cause damage in many different ways, for example:

 -  [setProducer()](https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L93-L99) - **Setting the producer to a malicious address:** when an external call is sent to the new malicious producer address via the claim function (producer.claimUserReward()) it could request to drain the users funds or other similarly exploitative functions.
 - [setRewardRecipientPriviliged()](https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexRewards.sol#L432-L453) - **Setting reward recipient to his own address** so the attacker can get the rewards distributed from the PirexGmx.sol contract.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Implement access control check for the initialize function of the contract. Long term, examine the use of initializers to ensure that they have proper access controls.