xanoutas/solidity-security-findings huggingface dataset:
`remove_gauge` will access wrong gauge types when removing gauge's weight 
Audit: 2024-03-neobase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-neobase/blob/main/src/GaugeController.sol#L224-L229


# Vulnerability details

## Impact
`remove_gauge` will remove the weight of non-existent gauge types due to incorrect execution order.

## Proof of Concept

When `remove_gauge` is called, it will first remove the `gauge_types_` of the provided `_gauge` address by setting it to 0. Then, it will remove the gauge weight by calling `_remove_gauge_weight`.

https://github.com/code-423n4/2024-03-neobase/blob/main/src/GaugeController.sol#L224-L229

```solidity
    function remove_gauge(address _gauge) external onlyGovernance {
        require(gauge_types_[_gauge] != 0, "Invalid gauge address");
>>>     gauge_types_[_gauge] = 0;
        _remove_gauge_weight(_gauge);
        emit GaugeRemoved(_gauge);
    }
```

However, calling `_remove_gauge_weight` will always remove the weight of non-existent gauge types because it accesses `gauge_types_` to get the gauge type, which has already been removed before this function is called.

https://github.com/code-423n4/2024-03-neobase/blob/main/src/GaugeController.sol#L350-L374

```solidity
    function _remove_gauge_weight(address _gauge) internal {
>>>     int128 gauge_type = gauge_types_[_gauge] - 1;
        uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK;

        uint256 old_weight_bias = _get_weight(_gauge);
        uint256 old_weight_slope = points_weight[_gauge][next_time].slope;
        uint256 old_sum_bias = _get_sum(gauge_type);

        points_weight[_gauge][next_time].bias = 0;
        points_weight[_gauge][next_time].slope = 0;

        uint256 new_sum = old_sum_bias - old_weight_bias;
        points_sum[gauge_type][next_time].bias = new_sum;
        points_sum[gauge_type][next_time].slope -= old_weight_slope;
        // We have to cancel all slope changes (gauge specific and global) that were caused by this gauge
        // This is not very efficient, but does the job for a governance function that is called very rarely
        for (uint256 i; i < 263; ++i) {
            uint256 time_to_check = next_time + i * WEEK;
            uint256 gauge_weight_change = changes_weight[_gauge][time_to_check];
            if (gauge_weight_change > 0) {
                changes_weight[_gauge][time_to_check] = 0;
                changes_sum[gauge_type][time_to_check] -= gauge_weight_change;
            }
        }
    }
```

This will cause the `remove_gauge` function to not properly remove gauge's weight and update gauge type's point sum.

## Tools Used

Manual review.

## Recommended Mitigation Steps

Set `gauge_types_` to 0 after `_remove_gauge_weight` is called.

```diff
    function remove_gauge(address _gauge) external onlyGovernance {
        require(gauge_types_[_gauge] != 0, "Invalid gauge address");
-       gauge_types_[_gauge] = 0;
        _remove_gauge_weight(_gauge);
+       gauge_types_[_gauge] = 0;
        emit GaugeRemoved(_gauge);
    }
```





## Assessed type

Error
hack3r-0m - faulty abi decoding from revert in catch block can lead to attacker controlled execution
Audit: 2023-02-gmx-judging
Severity: HIGH

hack3r-0m

high

# faulty abi decoding from revert in catch block can lead to attacker controlled execution

## Summary

faulty abi decoding from revert in catch block can lead to attacker controlled execution

## Vulnerability Detail

```solidity
        (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
```
attacker can return faulty abi encoded message from call such that it fails while decoding and hence allowing attacker to control when deposit is executed

## Impact

due to this, attacker do risk-free deposit when market conditions are favourable

## Code Snippet

https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/exchange/DepositHandler.sol#L186

## Tool used

Manual Review

## Recommendation

do not decode the reason

Duplicate of #119
hack3r-0m - while creating deposit, fee can be deducted in wrong manner if initialToken is not final token
Audit: 2023-02-gmx-judging
Severity: HIGH

hack3r-0m

high

# while creating deposit, fee can be deducted in wrong manner if initialToken is not final token

## Summary

while creating deposit, fee can be deducted in wrong manner if initialToken is not final token

## Vulnerability Detail

```solidity

        if (market.longToken == wnt) {
            initialLongTokenAmount -= params.executionFee;
        } else if (market.shortToken == wnt) {
            initialShortTokenAmount -= params.executionFee;
```

here, there is implied assumption that `longToken`  == `initialLongTokenAmount` which is not always true, they might be completly different tokens and have different decimals

## Impact

wrong amount of execution fee deducted

## Code Snippet

https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/deposit/ExecuteDepositUtils.sol#L392

## Tool used

Manual Review

## Recommendation

convert to WNT or deduct fee after swap takes place

Duplicate of #205
joestakey - Incorrect check in `claimCollateral` leads to the function always reverting
Audit: 2023-02-gmx-judging
Severity: HIGH

joestakey

high

# Incorrect check in `claimCollateral` leads to the function always reverting

## Summary
Due to a  logic error,`claimCollateral` always reverts, making it impossible for users to claim collateral.

## Vulnerability Detail
`MarketUtils.claimCollateral` computes the amount of collateral a user can claim before transferring that amount.

The issue is that the call mistakenly reverts when the `claimableAmount` is greater than the already claimed amount line 632.

```solidity
File: contracts/market/MarketUtils.sol
631:          uint256 adjustedClaimableAmount = Precision.applyFactor(claimableAmount, claimableFactor);
632:         if (adjustedClaimableAmount >= claimedAmount) {//@audit error
633:             revert CollateralAlreadyClaimed(adjustedClaimableAmount, claimedAmount);
634:         }
635:
636:        uint256 remainingClaimableAmount = adjustedClaimableAmount - claimedAmount;
```

## Impact
The call will always revert, either line 633, or with an underflow error line 636.
Users cannot claim their collateral.

## Code Snippet
https://github.com/gmx-io/gmx-synthetics/blob/7be3ef2d119d9e84473e1a49f346bcdc06fd57a3/contracts/market/MarketUtils.sol#L631-L636

## Tool used
Manual Review

## Recommendation
The call should revert if the `claimableAmount` is **lower** than the already claimed amount

```diff
631:          uint256 adjustedClaimableAmount = Precision.applyFactor(claimableAmount, claimableFactor);
-632:         if (adjustedClaimableAmount >= claimedAmount) {
+632:         if (adjustedClaimableAmount <= claimedAmount) {
633:             revert CollateralAlreadyClaimed(adjustedClaimableAmount, claimedAmount);
634:         }
635:
636:        uint256 remainingClaimableAmount = adjustedClaimableAmount - claimedAmount;
```


Duplicate of #136
simon135 - slippage set to 0 it can cause users to get sandwiched
Audit: 2023-02-gmx-judging
Severity: HIGH

simon135

high

# slippage set to 0 it can cause users to get sandwiched

## Summary

If the user wants to decrease their position and want  to swap 10 eth to their collateral token but slippage is set to 0 so they will get a bad price and can lose alot of their eth

## Vulnerability Detail

ex:
bob has pos and loses 1 eth and is left with 10 eth they want to decrease the pos and get their collateral back in usdc they will have to swap with slippage set to 0 so they will lose funds
it's even worse the markets get the funds back they won't be able to pay out the full amount and they will be in dos state until the protocol is made whole

## Impact

no slippage check

## Code Snippet

```solidity

     try params.contracts.swapHandler.swap(
                SwapUtils.SwapParams(
                    params.contracts.dataStore,
                    params.contracts.eventEmitter,
                    params.contracts.oracle,
                    Bank(payable(params.market.marketToken)),
                    pnlToken, // tokenIn
                    profitAmount, // amountIn
                    swapPathMarkets, // markets
                    0, // minOutputAmount
                    params.market.marketToken, // receiver
                    false // shouldUnwrapNativeToken

```

## Tool used

Manual Review

## Recommendation

add slippage checks and user validate min amount checks


Duplicate of #138
n33k - WNT in depositVault can be drained by abusing initialLongToken/initialShortToken of CreateDepositParams
Audit: 2023-02-gmx-judging
Severity: HIGH

n33k

high

# WNT in depositVault can be drained by abusing initialLongToken/initialShortToken of CreateDepositParams

## Summary

The attacker can abuse the `initialLongToken`/`initialShortToken` of `CreateDepositParams` to drain all the WNT from depositVault.

## Vulnerability Detail

```solidity
    function createDeposit(
        DataStore dataStore,
        EventEmitter eventEmitter,
        DepositVault depositVault,
        address account,
        CreateDepositParams memory params
    ) external returns (bytes32) {
        Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, params.market);

        uint256 initialLongTokenAmount = depositVault.recordTransferIn(params.initialLongToken);
        uint256 initialShortTokenAmount = depositVault.recordTransferIn(params.initialShortToken);

        address wnt = TokenUtils.wnt(dataStore);

        if (market.longToken == wnt) {
            initialLongTokenAmount -= params.executionFee;
        } else if (market.shortToken == wnt) {
            initialShortTokenAmount -= params.executionFee;
```

The `initialLongToken` and `initialShortToken` of `CreateDepositParams` can be set to any token address and there is no check for the `initialLongToken` and `initialShortToken` during `createDeposit`. The attacker can set `initialLongToken`/`initialShortToken` to a token(USDC e.g.) with less value per unit than WNT and for a market with `market.longToken == wnt` or `market.shortToken == wnt`, `params.executionFee` will be wrongly subtracted from `initialLongTokenAmount` or `initialLongTokenAmount`. This allows the attacker to have a controllable large `params.executionFee` by sending tokens with less value. By calling `cancelDeposit`, `params.executionFee` amount of WNT will be repaid to the attacker.

Here is a PoC test case that drains WNT from depositVault:

```diff
diff --git a/gmx-synthetics/test/router/ExchangeRouter.ts b/gmx-synthetics/test/router/ExchangeRouter.ts
index 7eca238..c40a71c 100644
--- a/gmx-synthetics/test/router/ExchangeRouter.ts
+++ b/gmx-synthetics/test/router/ExchangeRouter.ts
@@ -103,6 +103,82 @@ describe("ExchangeRouter", () => {
     });
   });
 
+  it("createDepositPoC", async () => {
+    // simulate normal user deposit
+    await usdc.mint(user0.address, expandDecimals(50 * 1000, 6));
+    await usdc.connect(user0).approve(router.address, expandDecimals(50 * 1000, 6));
+    const tx = await exchangeRouter.connect(user0).multicall(
+      [
+        exchangeRouter.interface.encodeFunctionData("sendWnt", [depositVault.address, expandDecimals(11, 18)]),
+        exchangeRouter.interface.encodeFunctionData("sendTokens", [
+          usdc.address,
+          depositVault.address,
+          expandDecimals(50 * 1000, 6),
+        ]),
+        exchangeRouter.interface.encodeFunctionData("createDeposit", [
+          {
+            receiver: user0.address,
+            callbackContract: user2.address,
+            market: ethUsdMarket.marketToken,
+            init
0xAmanda - Loading arbitrary's contract data to memory allows grieffing attack
Audit: 2023-02-gmx-judging
Severity: HIGH

0xAmanda

medium

# Loading arbitrary's contract data to memory allows grieffing attack

## Summary
Incorrect handling of returned data will cost more gas to keepers if callback contracts return an extense amount of data for the keeper to handle in the catch statement.

https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/callback/CallbackUtils.sol#L79

## Vulnerability Detail
In the functions from the callbackUtils.sol file, when execution fails, those functions load the memory data returned by the callbackContract(), which user can input to their own malicious contract returning extense data inside the gasLimit to increase the cost of processing that data for the keeper.

    function afterDepositExecution(bytes32 key, Deposit.Props memory deposit) internal {
        if (!isValidCallbackContract(deposit.callbackContract())) { return; }

        try IDepositCallbackReceiver(deposit.callbackContract()).afterDepositExecution{ gas: deposit.callbackGasLimit() }(key, deposit) {
        } catch (bytes memory reasonBytes) {
            (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
            emit AfterDepositExecutionError(key, deposit, reason, reasonBytes);
        }
    }

Therefore the attack is not profitable for the attacker but rather costs gas to the keeper. Because instead of reverting on failure, they are handling the error from the callbackContract() in the catch statement, loading the memory data, which costs gas.

## Impact
Increase the gas cost for the keeper in failed transactions by loading extra data to memory that callbackUtils handles.

## Code Snippet

https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/callback/CallbackUtils.sol#L65-L67

## Tool used

Manual Review

## Recommendation
It is not a good practice to load data from arbitrary contracts because it opens the attack vector for grieffing attacks. Consider  not loading that data and reverting on failure 


Duplicate of #141
0xAmanda - Incorrect function call leads to stale borrowing fees
Audit: 2023-02-gmx-judging
Severity: HIGH

0xAmanda

high

# Incorrect function call leads to stale borrowing fees

## Summary
Due to an incorrect function call while getting the total borrow fees, the returned fees will be an inaccurate and stale amount. Which will have an impact on liquidity providers


## Vulnerability Detail

As said the function  `getTotalBorrowingFees`:

    function getTotalBorrowingFees(DataStore dataStore, address market, address longToken, address shortToken, bool isLong) internal view returns (uint256) {
        uint256 openInterest = getOpenInterest(dataStore, market, longToken, shortToken, isLong);
        uint256 cumulativeBorrowingFactor = getCumulativeBorrowingFactor(dataStore, market, isLong);
        uint256 totalBorrowing = getTotalBorrowing(dataStore, market, isLong);
        return openInterest * cumulativeBorrowingFactor - totalBorrowing;
    }

calculates the fess by calling  `getCumulativeBorrowingFactor(...)`:

https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/market/MarketUtils.sol#L1890

which is the wrong function to call because it returns a stale borrowing factor. To get the actual borrowing factor and calculate correctly the borrowing fees, GMX should call the `getNextCumulativeBorrowingFactor` function:


https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/market/MarketUtils.sol#L1826


Which makes the right calculation, taking into account the stale fees also:

        uint256 durationInSeconds = getSecondsSinceCumulativeBorrowingFactorUpdated(dataStore, market.marketToken, isLong);
        uint256 borrowingFactorPerSecond = getBorrowingFactorPerSecond(
            dataStore,
            market,
            prices,
            isLong
        );

        uint256 cumulativeBorrowingFactor = getCumulativeBorrowingFactor(dataStore, market.marketToken, isLong);

        uint256 delta = durationInSeconds * borrowingFactorPerSecond;
        uint256 nextCumulativeBorrowingFactor = cumulativeBorrowingFactor + delta;
        return (nextCumulativeBorrowingFactor, delta);

## Impact
Ass fee calculation will not be accurate, liquidity providers will be have a less-worth token  because pending fees are not accounted in the pool's value


## Code Snippet
https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/market/MarketUtils.sol#L1888

## Tool used

Manual Review

## Recommendation

In order to mitigate the issue, call the function `getNextCumulativeBorrowingFactor` instead of the function `getCumulativeBorrowingFactor()` for a correct accounting and not getting stale fees
berndartmueller - Underestimated gas estimation for executing withdrawals leads to insufficient keeper compensation
Audit: 2023-02-gmx-judging
Severity: HIGH

berndartmueller

medium

# Underestimated gas estimation for executing withdrawals leads to insufficient keeper compensation

## Summary

The `GasUtils.estimateExecuteWithdrawalGasLimit` function underestimates the gas estimation for withdrawal execution, as it does not take into account token swaps, unlike the gas estimation in the `GasUtils.estimateExecuteDepositGasLimit` function (used to estimate executing deposits).

## Vulnerability Detail

When creating a withdrawal request, the `WithdrawalUtils.createWithdrawal` function estimates the gas required to execute the withdrawal and validates that the paid execution fee (`params.executionFee`) is sufficient to cover the estimated gas and to compensate the keeper executing the withdrawal fairly.

However, the `GasUtils.estimateExecuteWithdrawalGasLimit` function used to estimate the gas for executing withdrawals does not account for token swaps that can occur at the end of the withdrawal logic and therefore underestimates the gas estimation.

Token swaps are performed in the `WithdrawalUtils._executeWithdrawal` function in lines 354 and 365.

## Impact

The keeper executing withdrawals receives fewer execution fees and is not fully compensated for the gas spent. Moreover, users can pay fewer execution fees than expected and required.

## Code Snippet

[contracts/gas/GasUtils.sol#L150](https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/gas/GasUtils.sol#L150)

The gas estimate calculated in the `GasUtils.estimateExecuteWithdrawalGasLimit` function only uses a static gas limit plus the callback gas limit. Token swaps are not accounted for.

```solidity
149: function estimateExecuteWithdrawalGasLimit(DataStore dataStore, Withdrawal.Props memory withdrawal) internal view returns (uint256) {
150:     return dataStore.getUint(Keys.withdrawalGasLimitKey(false)) + withdrawal.callbackGasLimit();
151: }
```

[contracts/withdrawal/WithdrawalUtils.createWithdrawal() - L163](https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/withdrawal/WithdrawalUtils.sol#L163)

As observed in the `createWithdrawal` function, the `GasUtils.estimateExecuteWithdrawalGasLimit` function estimates the gas required to execute the withdrawal and validates the paid execution fee accordingly.

```solidity
110: function createWithdrawal(
111:     DataStore dataStore,
112:     EventEmitter eventEmitter,
113:     WithdrawalVault withdrawalVault,
114:     address account,
115:     CreateWithdrawalParams memory params
116: ) external returns (bytes32) {
...      // [...]
160:
161:     CallbackUtils.validateCallbackGasLimit(dataStore, withdrawal.callbackGasLimit());
162:
163:     uint256 estimatedGasLimit = GasUtils.estimateExecuteWithdrawalGasLimit(dataStore, withdrawal);
164:     GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee);
165:
166:     bytes32 key = NonceUtils.getNextKey(dataStore);
167:
168:     WithdrawalStoreUtils.set(dataStore, key, with
berndartmueller - Decreasing a position without a swap path is susceptible to slippage
Audit: 2023-02-gmx-judging
Severity: HIGH

berndartmueller

medium

# Decreasing a position without a swap path is susceptible to slippage

## Summary

The `DecreaseOrderUtils.processOrder` function does not check the output amount against the minimum output amount if the order does not have a swap path defined. This could lead to a situation where the user receives fewer tokens than expected, and the provided slippage protection parameter is not respected.

## Vulnerability Detail

Decreasing a position and specifying the order swap type `SwapCollateralTokenToPnlToken` swaps the withdrawn collateral to the PnL token. This swap is performed in `DecreasePositionCollateralUtils.swapWithdrawnCollateralToPnlToken` while `minOutputAmount` is set to `0`, which means that the swap is not protected against slippage.

Even though users are able to provide a slippage protection parameter in the order (`order.minOutputAmount()`), this parameter is not used in the `DecreaseOrderUtils.processOrder` function if the order does not have a swap path (`order.swapPath()`) defined. This means that users safely assume that their order is protected against slippage, while in this case, it is not.

## Impact

Users receive fewer tokens than expected due to slippage even though they provided a slippage protection parameter.

## Code Snippet

[contracts/order/DecreaseOrderUtils.processOrder(..) - L76-L81](https://github.com/sherlock-audit/2023-02-gmx/blob/main/gmx-synthetics/contracts/order/DecreaseOrderUtils.sol#L76-L81)

If the order does not have a swap path, the output token is immediately transferred to the receiver. However, the output amount (`result.outputAmount`) is not checked against the minimum output amount (`order.minOutputAmount()`) specified in the order. This could lead to a situation where the receiver receives fewer tokens than expected.

```solidity
020: function processOrder(BaseOrderUtils.ExecuteOrderParams memory params) external {
...      // [...]
037:
038:     DecreasePositionUtils.DecreasePositionResult memory result = DecreasePositionUtils.decreasePosition(
039:         PositionUtils.UpdatePositionParams(
040:             params.contracts,
041:             params.market,
042:             order,
043:             params.key,
044:             position,
045:             positionKey
046:         )
047:     );
048:
...      // [...]
074:
075:     if (order.swapPath().length == 0) {
076:         MarketToken(payable(order.market())).transferOut( // @audit-info Missing slippage protection
077:             result.outputToken,
078:             order.receiver(),
079:             result.outputAmount,
080:             order.shouldUnwrapNativeToken()
081:         );
082:     } else {
083:         try params.contracts.swapHandler.swap(
084:             SwapUtils.SwapParams(
085:                 params.contracts.dataStore,
086:                 params.contracts.eventEmitter,
087:                 params.contracts.oracle,
088:                 Bank(payable(order.market())),
089:                 result.outp