xanoutas/solidity-security-findings huggingface dataset:
The `RandomizerNXT.sol` doesn't provide randomness
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerNXT.sol#L55-L59
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L444-L446
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L192-L193
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L228-L229
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerNXT.sol#L55-L59
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/XRandoms.sol#L35-L38
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/XRandoms.sol#L40-L43


# Vulnerability details

## Impact
The `RandomizerNXT.sol` contract doesn't provide a true randomness source. In a context where true randomness is required, this contract is not appropriate to deliver a random hash. The reason is because the minting and hash generation happen all within one transaction and if not satisfactory to the odds abuser could be reverted. 

The `mint()` function is here called, 

```
    function mint(uint256 mintIndex, address _mintingAddress , address _mintTo, string memory _tokenData, uint256 _saltfun_o, uint256 _collectionID, uint256 phase) external {
        require(msg.sender == minterContract, "Caller is not the Minter Contract");

        ...

        if (collectionAdditionalData[_collectionID].collectionTotalSupply >= 
        collectionAdditionalData[_collectionID].collectionCirculationSupply) {
@>         _mintProcessing(mintIndex, _mintTo, _tokenData, _collectionID, _saltfun_o);

            ...
    }
```
as we can see the mint function calls the `_mintProcessing()`

```
    function _mintProcessing(uint256 _mintIndex, address _recipient, string memory _tokenData, uint256 _collectionID, uint256 _saltfun_o) internal {
      
       ...

 @>       collectionAdditionalData[_collectionID].randomizer.calculateTokenHash(_collectionID, _mintIndex, _saltfun_o);
       ...
    }



```
The `randomizer` contract for the given collection is thereafter called...

```
    function calculateTokenHash(uint256 _collectionID, uint256 _mintIndex, uint256 _saltfun_o) public {
        require(msg.sender == gencore);
@>       bytes32 hash = keccak256(abi.encodePacked(_mintIndex, blockhash(block.number - 1), randoms.randomNumber(), randoms.randomWord()));
        gencoreContract.setTokenHash(_collectionID, _mintIndex, hash);
    }
```
The hash can lastly be checked in the core contract by anyone in the `retrieveTokenHash()`.

This is where the 'magic' happens. A `random` hash is calculated, through the combination of hashed block timestamps, block hashes and a string, calculation made in the `XRandom`
Deposit can be front-runned
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The depositor who got frontrun by the attacker will lose all their funds. And all the future depositors.
An attacker can front run the first user's `deposit` and transfer tokens to the vault directly causing all future depositors to lose all their funds.

## Proof of Concept
In `deposit`, if there are some existing balance in the erc4626 contract, say 1 wei of the token, the supply before transfer will be 1, since total shares == 0, newShares will always be 0.

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

## Tools Used
manual review

## Recommended Mitigation Steps
Check whether a mint is the first mint (`assets == 0`) and handle it correctly.
´userAccrue` rewards manipulation
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details



## Impact
A flashloan can be used to set a huge last balance which later will accrue a huge reward.

## Proof of Concept
Buy lots of a rewards-producing token, possibly by means of a flashloan. Call `PirexRewards.userAccrue()` which sets `u.lastBalance` to this now very high value. Sell back the tokens and return rthe flashloan.
Next time `PirexRewards.userAccrue()` is called the reward will be very high.

```solidity
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);
}
```

`Similarly in PxGmxRewards._userAccrue()`

## Tools Used
Code inspection

## Recommended Mitigation Steps
A malicious early user/attacker can manipulate the pxGmx's pricePerShare to take an unfair share of future user's deposits
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L164-L166
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/PirexERC4626.sol#L167-L176
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L173-L191
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L339-L362


# Vulnerability details

## Impact

An attacker/early user can deposit 1 wei in the vault and increase the price per share by sending a very high value of the underlying directly to the vault, causing next vault depositors to:
- not be able to deposit less than the very high share price set by the attacker.
- lose value due to rounding error.


`redeem` uses `previewRedeem` to calculate assets per shares.

[AutoPxGmx.sol#L339-L362](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/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;
        }


        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");


        _burn(owner, shares);


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


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

`previewRedeem` uses `convertToAssets` to do the conversion from shares to assets.

[AutoPxGmx.sol#L173-L191](https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L173-L191)
```solidity
    function previewRedeem(uint256 shares)
        public
        view
        override
        returns (uint256)
    {
        // Calculate assets based on a user's % ownership of vault shares
        uint256 assets = convertToAssets(shares);


        uint256 _totalSupply = totalSupply;


        // Calculate a penalty - zero if user is the last to withdraw
        uint256 penalty = (_totalSupply == 0 || _totalSupply - shares == 0)
            ? 0
            : assets.mulDivDown(withdrawalPenalty, FEE_DENOMINATOR);


        // Redeemable amount is the post-penalty amount
        return assets - penalty;
    }
```

`convertToAssets` do the calculation using `totalAssets`.

[PirexERC4626.sol#L167-L176](https://github.com/code-423n4/2022-11-red
Potential mismanaging of the access and roles
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L921
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L940
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L956


# Vulnerability details

## Impact
In `initiateMigration()` method we are setting `migratedTo` to an address of the potential migration contract
Later on, if `completeMigration()` is called, it basically checks if the provided `oldContract` is equal to `migratedTo` when we enter the `migrateReward()` method.
There is a possibility that the address value provided for `oldContract` in `initiateMigration()` and `completeMigration()` differ
This could cause a problem when accepting the transfer [on this line](https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L967) since the check happens when `migrateReward()` is called

## Proof of Concept
On lines:
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L921
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L940
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L956
There should be
## Tools Used

## Recommended Mitigation Steps
`gmxBaseReward` must not be the same as `asset`
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGmx.sol#L260-L288
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/vaults/AutoPxGlp.sol#L232-L250


# Vulnerability details



## Impact
Compounding will attempt to swap/deposit all assets instead of just the rewards, which reverts because of integer overflow, which causes withdrawals to revert.

## Proof of Concept
In `AutoPxGmx.compound()`:
```solidity
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)
    );
}

// Only distribute fees if the amount of vault assets increased
if ((totalAssets() - assetsBeforeClaim) != 0) {
```
we see that if `gmxBaseReward` is the same token as `asset` (e.g. WETH, WAVAX) then the entire balance of `asset` will be swapped. Then the last line above `if ((totalAssets() - assetsBeforeClaim) != 0) {` will revert as now `totalAssets()` will be 0 (unless this is compounded before a first deposit). `compound()` is called in `withdraw()` and `redeem()` thus causing these to revert. However, a first deposit is possible since `compound()` doesn't revert in `beforeDeposit()` at the first deposit since assets are 0.

We have the same problem in `AutoPxGlp.compound()`:
```solidity
uint256 preClaimTotalAssets = asset.balanceOf(address(this));
uint256 preClaimPxGmxAmount = pxGmx.balanceOf(address(this));

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

// Track the amount of rewards received
gmxBaseRewardAmountIn = gmxBaseReward.balanceOf(address(this));

if (gmxBaseRewardAmountIn != 0) {
    // Deposit received rewards for pxGLP
    (, pxGlpAmountOut, ) = PirexGmx(platform).depositGlp(
        address(gmxBaseReward),
        gmxBaseRewardAmountIn,
        minUsdg,
        minGlp,
        address(this)
    );
}

// Distribute fees if the amount of vault assets increased
uint256 newAssets = totalAssets() - preClaimTotalAssets;
```

## Tools Used
Code inspection

## Recommended Mitigation Steps
Check balance before and take the difference of this and the new balance.
Price manipulation can lead to users lossing funds
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

# [H] Price manipulation can lead to users lossing funds

## Impact
Early malicious user will profit from future users' deposits while future users' will loose funds/value.

## Proof of Concept
An early user can call the `deposit` function of any of the ERC4626 vaults with only `1 wei` of the asset and respectively get `1 wei` of the shares in return. After that the attacker may send `10000 * 10 ** 18 wei` of the asset to inflate the share price from `1` to `1 * 10 ** 22` (`(1 + 10000e18 - 1) / 1`). The next user who deposits some amount of asset will receive significatly less shares - if the next user deposits `20000 * 10 ** 18 wei` of the asset they will receive only 2 wei of shares which means they have lost half of their money if they redeem right after that.

https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/PirexERC4626.sol#L60-L78 \
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/PirexERC4626.sol#L80-L97

## Tools Used
manula review

## Recommended Mitigation Steps
Require a minimum amount of initial deposit and burn a portion of the initial shares.

```
    function deposit(uint256 assets, address receiver)
        public
        virtual
        returns (uint256 shares)
    {
        if (totalAssets() != 0) beforeDeposit(receiver, assets, shares);

        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");
        
        if (totalAssets() == 0) {
            _mint(address(0), initialShares);
        }

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(receiver, assets, shares);
    }

    function mint(uint256 shares, address receiver)
        public
        virtual
        returns (uint256 assets)
    {
        if (totalAssets() != 0) beforeDeposit(receiver, assets, shares);

        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.
        
        if (totalAssets() == 0) {
          _mint(address(0), initialShares);
        }

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(receiver, assets, shares);
    }
```

## Reference
Reference to how another protocol has fixed this same issue: \
https://github.com/sentimentxyz/protocol/pull/232/commits/16557a3e82dce3c6a03d5e8f7280e1af12fe3bf0
Reentrancy potential
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
A reentrancy attack could be performed and can result in exploit and losing funds
## Proof of Concept
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L339
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/vaults/AutoPxGmx.sol#L315
https://github.com/code-423n4/2022-11-redactedcartel/blob/main/src/PirexGmx.sol#L824
## Tools Used
Manual review
## Recommended Mitigation Steps
Use `nonReentrant` modifier imported from `solmate/utils/ReentrancyGuard.sol`
`AutoPxGlp` and `AutoPxGmx` are not compliant with ERC4626 standard
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

### Proof of Concept

In both contracts, the `previewWithdraw` function has the same body

```solidity
// Calculate shares based on the specified assets' proportion of the pool
        uint256 shares = convertToShares(assets);

        // Save 1 SLOAD
        uint256 _totalSupply = totalSupply;

        // Factor in additional shares to fulfill withdrawal if user is not the last to withdraw
        return
            (_totalSupply == 0 || _totalSupply - shares == 0)
                ? shares
                : (shares * FEE_DENOMINATOR) /
                    (FEE_DENOMINATOR - withdrawalPenalty);
```

The problem is that the ERC4626 standard specifically requires `previewWithdraw` to round up, while this code does not do so - actually `convertToShares` rounds down.

`previewWithdraw` returns the amount of shares that would be burned to withdraw specific amount of asset. Thus, the amount of shares must to be rounded up, so that the vault won't be shortchanged.

### Impact

Since contracts are not compliant with a standard this can result in some integration issues and in this particular case it can also result in a wrong amount returned from `previewWithdraw` which won’t actually work with `withdraw`, so it can cause lots of reverted transactions. Medium severity is appropriate here.

### Recommendation

Make the `previewWithdraw` method round up, the same way it does in `PirexERC4626.sol`
Code is vulnerable to ERC4626 first depositor shares manipulation exploit
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

### Proof of Concept

The normal ERC4626 implementation (which is not changed in the repository) has a vulnerability which can result in the first depositor stealing every subsequent depositor’s funds.

It works like this:

1. Vault is just deployed and Bob deposits just 1 wei of `underlying`, so he now holds 1 share
2. Alice is about to deposit 1000 * 1e18 worth of `underlying`
3. Bob sees this in the mempool and front runs her transaction with a direct ERC20::transfer to the vault for 1000 * 1e18 tokens
4. Now her deposit will result in 0 shares, because the amount she deposited was less than the balance of the contract
5. Now Bob backruns her deposit by redeeming the whole `underlying` balance for his 1 share (the total supply) resulting in him stealing all of Alice’s deposited tokens

### Impact

This can result in a 100% loss of deposited funds for users of the protocol, so it should be of High severity.

### Recommendation

Revert when the shares minted are zero.
Incorrect rounding while withdrawing assets from `AutoPxGmx` and `AutoPxGlp` contracts
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

The function `previewWithdraw` is overridden in the `AutoPxGmx` and `AutoPxGlp` contracts to account for penalty fees while exiting the vaults. This happens in line 212 of the `AutoPxGmx` contract and similarly in line 190 of the `AutoPxGlp` contract:

```solidity
return
    (_totalSupply == 0 || _totalSupply - shares == 0)
        ? shares
        : (shares * FEE_DENOMINATOR) /
            (FEE_DENOMINATOR - withdrawalPenalty);
```

Here, the integer division will round down, while the correct behavior should be to round up in favor of the protocol.

## Impact

This issue would allow a user to withdraw tokens without paying penalties, which should not represent a big impact in a realistic scenario since it would need to involve handling extremely low quantities of assets/shares or withdrawing assets in batches of a relatively small amount (which should be discouraged by gas costs).

During each call to `withdraw`, if the division is not exact, the protocol will be losing 1 share of penalties due to rounding. 

The correct way should be to round up the division, following the base ERC4626 implementation of the `previewWithdraw` function which uses `mulDivUp`.

## Proof of Concept

The following test demonstrates the issue. In this case the user mints 10 shares and then withdraws 1 token by burning a single share without any penalty.


```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import "forge-std/Test.sol";

import {ERC20} from "solmate/tokens/ERC20.sol";
import {AutoPxGmx} from "src/vaults/AutoPxGmx.sol";
import {PirexGmx} from "src/PirexGmx.sol";
import {Helper} from "./Helper.sol";

contract AuditTest is Helper {
    function test_AutoPxGmx_WithdrawWrongRounding() public {        
        address user = testAccounts[0];

        uint256 userBalance = 10;
        _mintPx(user, userBalance, true);
                
        // Approve contract
        vm.prank(user);
        pxGmx.approve(address(autoPxGmx), type(uint256).max);
        
        // User deposits balance, he gets 10 shares
        vm.prank(user);
        autoPxGmx.deposit(userBalance, user);        
        assertEq(autoPxGmx.balanceOf(user), userBalance);
        
        vm.prank(user);
        uint256 shares = autoPxGmx.previewWithdraw(1);
        
        // Only 1 share needed to withdraw 1 token
        assertEq(shares, 1);
        
        vm.prank(user);
        autoPxGmx.withdraw(1, user, user);
        
        // User has withdrawn 1 token and still has 9 shares 
        assertEq(pxGmx.balanceOf(user), 1);
        assertEq(autoPxGmx.balanceOf(user), 9);   
    }
}
```

## Recommended Mitigation Steps

Change the penalty calculation in the `previewWithdraw` function to round up:

```solidity
return
    (_totalSupply == 0 ||